SEW Maturaskript
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Softwareentwicklung Maturaskript 2020/21 1. [Java | Quarkus](#1-Java-|-Quarkus) 1. [Konfiguration](#1.1-Konfiguration) 2. [Java Syntax](#1.2-Java-Syntax) 3. [Entities](#1.3-Entities) 4. [JPA](#1.4-JPA) 5. [Repository](#1.5-Repository) 6. [Services](#1.6-Services) 7. [Websockets](#1.7-Websockets) 8. [SSE](#1.8-SSE) 9. [Testen](#1.9-Testen) 10. [JSON Web Tokens (JWT)](#1.10-JSON-Web-Tokens) 11. [Sonstiges](#1.11-Sonstiges) 2. [Angular](#2-Angular) 1. [Testumgebung starten](#2.1-Testumgebung-starten) 2. [CLI](#2.2-CLI) 3. [Entity](#2.3-Entity) 4. [HTTP-Service](#2.4-HTTP-Service) 5. [Data-Service](#2.5-Data-Service) 6. [Binding](#2.6-Binding) 7. [Pipes](#2.7-Pipes) 8. [Angular Material Schematics](#2.8-Angular-Material-Schematics) 9. [Typescript Syntax](#2.9-Typescript-Syntax) 10. [Routing](#2.10-Routing) 11. [App-Module](#2.11-App-Module) 12. [Websockets](#2.12-Websockets) 13. [SSE](#2.13-SSE) 14. [Authentication (JWT)](#2.14-Authentication-(JWT)) 15. [HTML Cheatsheet](#2.15-HTML-Cheatsheet) 1 Java | Quarkus ============== 1.1 Konfiguration ------------- ### Extensions Beim Anlegen eines neuen Projekts folgende Extensions auswählen: - RESTEasy JSON-B - JDBC Driver - Derby - Hibernate ORM with Panache (falls ohne Panache gearbeitet wird, nur Hibernate ORM) - Websockets - SmallRye JWT (falls JWT erforderlich ist) ### Datenbank **Port Derby:** 1527 **Host:** localhost **Database:** databaseName `startNetworkServer` ausführen ### Application Properties Folgende Konfigurationen in application.properties eintragen: application.properties ```dockerfile= quarkus.datasource.db-kind=derby quarkus.datasource.jdbc.url=jdbc:derby://localhost:1527/reservationdb;create=true quarkus.hibernate-orm.database.generation=drop-and-create (none) quarkus.http.cors=true ``` 1.2 Java Syntax ------ ### Array ```java= // Array mit bestimmter Länge int[] arr = new int[5]; // Array mit unbestimmter Länge String[] cars = {"Volvo", "BMW", "Ford", "Mazda"}; // Schleife über Array for (String i : cars) {} // Mehrdimensional int[][] myNumbers = { {1, 2, 3, 4}, {5, 6, 7} }; int x = myNumbers[1][2]; String[][] personen = { { "Klaus", "Meyer", "Schlossallee. 4", "12345 Janzweitweg" }, { "Franz", "Schmitz", "Elisenstr. 18", "98765 Posenuckel" }, ``` ### Listen ```java= LinkedList<String> cars = new LinkedList<String>(); cars.add("Volvo"); // Remove the head using remove() cars.remove(); list.remove(4); list.remove("Geeks"); LinkedList<String> linkedList = new LinkedList<>(); for (int i = 0; i < linkedList.size(); i++) { System.out.println(linkedList.get(i)); } Iterator<String> iterator = linkedList.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } List<String> list = new ArrayList<>(); list.add("str 1"); list.add("str 2"); list.add(0,"str 3"); // Add 3 on position 0 list.remove(1); // remove item on position 1 list.remove("str 2"); // remove first occurrence of str 2 ``` ### Sets ```java= Set<String> cars = new HashSet<String>(); cars.add("Mazda"); cars.contains("Mazda"); cars.remove("Volvo"); ``` ### Maps Dient als Key-Value Objekt ```java= HashMap<String, String> capitalCities = new HashMap<String, String>(); capitalCities.put("England", "London"); capitalCities.get("England"); capitalCities.remove("England"); ``` 1.3 Entities -------- Bei einer Entity Klasse sollten allgemein 4 Punkte beachtet werden: - Annotation @Entity - Annotation @Id (nicht erforderlich mit Panache Entity) - Standardkonstruktor - extends PanacheEntity Bei der Verwendung von static, final, transient oder @Transient wird ein Feld nicht in die DB gespeichert ### @Column Annotation Da gewisse Bezeichnungen (wie bspw. User) vom System reserviert sind und somit nicht verwendet werden dürfen kann man Felder einen anderen Namen in der DB geben. Außerdem kann man in der @Column-Annotation beispielsweise die Länge festlegen, sowie unique, precision, scale und nullable state konfigurieren. @Table: Benennung der Datenbanktabelle name: Name der Tabelle Beispiel: ```java= @Column(name = "benutzer", nullable = false, unique = true, length = 255) private String user; @Table(name="Reservation") ``` ### Weitere wichtige Annotationen **Datumformat:** @JsonbDateFormat(“yyyy-MM-dd HH:mm”)\ **@JsonbTransient** Wenn ein Objekt nicht serialisiert werden soll. **@Basic:** erlaubt das setzen von Attributen wie optional: setzt entsprechend der Wahl NOT NULL; fetch: eager oder lazy ```java @Column(name="STARTTIME") @JsonbDateFormat("yyyy-MM-dd HH:mm") public LocalDateTime start; @ManyToOne @JsonbTransient public Customer customer; ``` ### Ansatz mit Panache If you don’t want to bother defining getters/setters for your entities, you can make them extend PanacheEntityBase and Quarkus will generate them for you. You can even extend PanacheEntity and take advantage of the default ID it provides. Your attributes must be public. Example for PanacheEntity: ```java= package at.htl.entities; import io.quarkus.hibernate.orm.panache.PanacheEntity; import javax.json.bind.annotation.JsonbDateFormat; import javax.json.bind.annotation.JsonbTransient; import javax.persistence.*; import java.time.LocalDateTime; @Entity public class Reservation extends PanacheEntity { @ManyToOne @JsonbTransient public Customer customer; @ManyToOne public Court court; @Column(name="STARTTIME") @JsonbDateFormat("yyyy-MM-dd HH:mm") public LocalDateTime start; @Column(name="ENDTIME") @JsonbDateFormat("yyyy-MM-dd HH:mm") public LocalDateTime end; public Reservation() { } public Reservation(Customer customer, LocalDateTime start, LocalDateTime end) { this.customer = customer; this.start = start; this.end = end; } } ``` As mentioned before you can specify your own ID strategy by extending PanacheEntityBase instead of PanacheEntity. Then you just declare whatever ID you want as a public field: ```java= @Entity public class Person extends PanacheEntityBase { @Id @SequenceGenerator( name = "personSequence", sequenceName = "person_id_seq", allocationSize = 1, initialValue = 4) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "personSequence") public Integer id; //... } ``` If you’re using repositories, then you will want to extend PanacheRepositoryBase instead of PanacheRepository and specify your ID type as an extra type parameter: ```java= @ApplicationScoped public class PersonRepository implements PanacheRepositoryBase<Person,Integer> { //... } ``` 1.4 JPA --- ### Beziehungen #### 1:1 Beziehung unidirektional <img src="https://i.ibb.co/TBcc3CD/unidirektional11.png" width="400"> #### 1:1 Beziehung bidirektional <img src="https://i.ibb.co/41hVtfH/bidirektional11.png" width="400"> #### 1:n Beziehung unidirektional <img src="https://i.ibb.co/2Z0sY5g/unidirektional1n.png" width="400"> #### 1:n Beziehung bidirektional <img src="https://i.ibb.co/p0CSwkD/bidirektional1n.png" width="400"> #### m:n Beziehung unidirektional <img src="https://i.ibb.co/2Kjwzrt/unidirektionalnm.png" width="400"> #### m:n Beziehung bidirektional <img src="https://i.ibb.co/xmjTc2P/bidirektionalnm.png" width="400"> JPA bietet die Möglichkeit, im Mapping anzugeben, ob eine Beziehung gleich mitgeladen werden soll, wenn die eigentliche Entität geladen wird. Dies ist möglich, indem man im Mapping den FetchType.EAGER angibt. ### JPA Named Queries Sollen mehrere Named Queries definiert werden, müssen diese unter einer @NamedQueries-Annotation gekapselt werden. <img src="https://i.ibb.co/WKnN2Tk/namedqueries.png" width="500"> Einsatz von Named Queries ![Named Queries Execution<span data-label="fig:namedqueriesExecution"></span>](https://i.ibb.co/fNfSCzM/named-Queries-Execution.png) Weitere Beispiele: ```java= public List<Ressource> findAllRessource(){ return em.createNamedQuery("Ressource.findAll", Ressource.class).getResultList(); } @Transactional public void deleteAllRessource(){ Query q1 = em.createQuery("DELETE FROM Ressource"); q1.executeUpdate(); } ``` Im nächsten Eintrag wird der auch der Einsatz von TypedQueries beschrieben. ### JPA Cascade Types * **CascadeType.PERSIST**: cascade type presist means that save() or persist() operations cascade to related entities. * **CascadeType.REMOVE**: cascade type remove removes all related entities association with this setting when the owning entity is deleted. * **CascadeType.ALL**: cascade type all is shorthand for all of the above cascade operations. Example: `@OneToMany(cascade=CascadeType.REMOVE)` ### JPA Fetch Types **EAGER** loading of collections means that they are fetched fully at the time their parent is fetched. So if you have Course and it has List<Student>, all the students are fetched from the database at the time the Course is fetched. **LAZY** loading Associated data loads only when we explicitly call getter or size method. Example: `@OneToMany(fetch = FetchType.LAZY)` 1.5 Repository ---------- ### Ansatz mit Panache When using Repositories, you get the exact same convenient methods as with the active record pattern, injected in your Repository, by making them implements PanacheRepository: ```java= @ApplicationScoped public class PersonRepository implements PanacheRepository<Person> { // put your custom logic here as instance methods public Person findByName(String name){ return find("name", name).firstResult(); } public List<Person> findAlive(){ return list("status", Status.Alive); } public void deleteStefs(){ delete("name", "Stef"); } } ``` Panache.getEntityManager(): Zugriff auf Enity Manager Instanz: ```java= package at.htl.repositories; import javax.enterprise.context.ApplicationScoped; import javax.persistence.TypedQuery; import javax.transaction.Transactional; import at.htl.entities.*; import io.quarkus.hibernate.orm.panache.Panache; import io.quarkus.hibernate.orm.panache.PanacheRepository; import java.time.LocalDateTime; import java.util.List; @ApplicationScoped public class CourtRepository implements PanacheRepository<Court> { @Transactional public void update(Court court){ Panache.getEntityManager().merge(court); } public List<Court> getAvailableCourts(long courtTypeId, LocalDateTime start, LocalDateTime end) { String q = "select c from Court c where c.courtType.id=?1 " + " and c NOT IN ( " + " select r.court from Reservation r " + " where r.court.courtType.id = ?1" + " and (?2 < r.end " + " and ?3 > r.start)" + " )"; TypedQuery<Court> query = getEntityManager().createQuery(q, Court.class); query.setParameter(1, courtTypeId); query.setParameter(2, start); query.setParameter(3, end); return query.getResultList(); } } ``` Wie in diesem Beispiel angeführt, können so TypedQueries ausgeführt werden. Weitere Beispiele: ```java= public List<Court> findAvailableCourts(long courtTypeId, LocalDateTime start, LocalDateTime end) { Query query = Panache.getEntityManager().createQuery("SELECT c " + "FROM Court c " + "LEFT JOIN Reservation r on r.court.id = c.id " + "JOIN c.courtType ct " + "where ct.id = :cTID AND ((r.start < :start AND r.end < :start OR r.end > :end AND r.start > r.end) or (r.id is null))"); query.setParameter("start", start); query.setParameter("end", end); query.setParameter("cTID", courtTypeId); return query.getResultList(); } public Object getLastSettlement() { Query query = Panache.getEntityManager().createQuery("select s from Settlement s where date in (select max(date) from Settlement )"); return query.getSingleResult(); } public void deleteSettlementData(){ Query query1 = Panache.getEntityManager().createQuery("delete from Ride"); Query query2 = Panache.getEntityManager().createQuery("delete from Repair"); Query query3 = Panache.getEntityManager().createQuery("delete from Fuel"); query1.executeUpdate(); query2.executeUpdate(); query3.executeUpdate(); } ``` 1.6 Services -------- ```java= package at.htl.services; import at.htl.entities.Court; import at.htl.entities.TimeslotDTO; import at.htl.repositories.CourtRepository; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.transaction.Transactional; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import java.net.URI; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.LinkedList; import java.util.List; @ApplicationScoped @Path("/api/court") public class CourtService { @Inject CourtRepository courtRepository; @GET @Produces(MediaType.APPLICATION_JSON) @Path("{id}") public Court find(@PathParam("id") long id){ return courtRepository.findById(id); } @GET @Produces(MediaType.APPLICATION_JSON) public List<Court> findAll(){ return courtRepository.listAll(); } @GET @Path("/freeTimeslots/{ctypeid}/{date}/{duration}") @Produces(MediaType.APPLICATION_JSON) public List<TimeslotDTO> getFreeTimeslots( @PathParam("ctypeid") long courtTypeId, @PathParam("date") String sDate, @PathParam("duration") int duration) { List<TimeslotDTO> slots = new LinkedList<>(); LocalDate date = LocalDate.parse(sDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); LocalDateTime start = date.atTime(15, 0); LocalDateTime end = date.atTime(22, 1); LocalDateTime actTime = start; while (start.plusMinutes(duration).isBefore(end)) { List<Court> courts = courtRepository.getAvailableCourts(courtTypeId, start, start.plusMinutes(duration)); if (courts != null && courts.size() > 0) { slots.add(new TimeslotDTO(start, start.plusMinutes(duration), courts)); } start = start.plusMinutes(30); } return slots; } @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Transactional public Response createCourt(Court court, @Context UriInfo uriInfo){ courtRepository.persist(court); URI uri = uriInfo.getAbsolutePathBuilder().path(Long.toString(court.id)).build(); return Response.created(uri).entity(court).build(); } @DELETE @Path("{id}") @Produces(MediaType.APPLICATION_JSON) @Transactional public Response deleteCourt(@PathParam("id") long id){ courtRepository.deleteById(id); return Response.status(Response.Status.NO_CONTENT).build(); } @PUT @Consumes(MediaType.APPLICATION_JSON) @Transactional public Response updateCourt(Court c){ courtRepository.update(c); return Response.status(Response.Status.NO_CONTENT).build(); } } ``` Anstatt mehrerer PathParams kann auch eine simple DTO Klasse verwendet werden: ```java= public class TimeslotDTO { private LocalDateTime starttime; private LocalDateTime endtime; private long[] courts; public TimeslotDTO() { courts = new long[0]; } public TimeslotDTO(LocalDateTime starttime, LocalDateTime endtime, List<Court> courts) { this.starttime = starttime; this.endtime = endtime; this.courts = courts.stream().mapToLong(value -> value.id).toArray(); } //Getter und Setter } ``` Alternative: Get-Request mit QueryParam.\ Bsp: http://localhost:8080/rest/getUser?id=1 ```java= @Path("getUser") @GET @Produces (MediaType.APPLICATION_JSON) public Person findOne(@QueryParam("id") long id) { return courtRepository.findById(id); } ``` 1.7 Websockets ---------- If you already have your Quarkus project configured, you can add the websockets extension to your project by running the following command in your project base directory: `./mvnw quarkus:add-extension -Dextensions="websockets"` This will add the following to your pom.xml: ```xml= <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-websockets</artifactId> </dependency> ``` ### Handling web sockets Our application contains a single class that handles the web sockets. Create the org.acme.websockets.ChatSocket class in the src/main/java directory. ChatSocket.java: ```java= package at.htl.services; import javax.enterprise.context.ApplicationScoped; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @ServerEndpoint("/chat/{username}") @ApplicationScoped public class ChatSocket { Map<String, Session> sessions = new ConcurrentHashMap<>(); private void broadcast(String message) { sessions.values().forEach(s -> { s.getAsyncRemote().sendObject(message , result -> { if (result.getException() != null) { System.out.println("Unable to send"); } }); }); } @OnOpen public void onOpen(Session session, @PathParam("username") String username) { sessions.put(username, session); System.out.println("User " + username + " joined"); broadcast("User " + username + " joined"); } @OnClose public void onClose(Session session, @PathParam("username") String username) { sessions.remove(username); broadcast("User " + username + " left"); } @OnMessage public void onMessage(String message, @PathParam("username") String username) { broadcast(">> " + username + ": " + message); } @OnError public void onError(Session session, @PathParam("username") String username, Throwable throwable) { sessions.remove(username); broadcast("User " + username + " left on error: " + throwable); } } ``` 1.8 SSE --- ```java= package at.htl.services; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.sse.OutboundSseEvent; import javax.ws.rs.sse.Sse; import javax.ws.rs.sse.SseBroadcaster; import javax.ws.rs.sse.SseEventSink; @Path("/sse") public class SseResource { private Sse sse; private volatile SseBroadcaster sseBroadcaster; private Integer id = 1; private OutboundSseEvent.Builder eventBuilder; @Context public void setSse(Sse sse) { this.sse = sse; this.eventBuilder = sse.newEventBuilder(); if (sseBroadcaster == null) { this.sseBroadcaster = sse.newBroadcaster(); } } @GET @Path("/subscribe") @Produces(MediaType.SERVER_SENT_EVENTS) public void subscribe(@Context SseEventSink sseEventSink) { this.sseBroadcaster.register(sseEventSink); this.broadcast(" members joined"); } private void broadcast(String msg) { try { OutboundSseEvent event = eventBuilder // .name("EventName") .id("" + ++id) .mediaType(MediaType.APPLICATION_JSON_TYPE) .data(id + " " + msg).build(); sseBroadcaster.broadcast(event); } catch (Exception e) { e.printStackTrace(); } } } ``` 1.9 Testen ------ Press Alt + Einfg to generate a test class. Example: ```java= package at.htl; import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; @QuarkusTest public class ExampleResourceTest { @Test public void testHelloEndpoint() { given() .when().get("/hello") .then() .statusCode(200) .body(is("hello")); } @Test public void testGreetingEndpoint() { String uuid = UUID.randomUUID().toString(); given() .pathParam("name", uuid) .when().get("/hello/greeting/{name}") .then() .statusCode(200) .body(is("hello " + uuid)); } @Test void create() { JsonObject tt = Json.createObjectBuilder() .add("userid", "if1001") .add("firstname", "Tom") .add("lastname", "Tester") .build(); given().contentType(MediaType.APPLICATION_JSON) .body(tt.toString()) .when() .post("/students") .then() .statusCode(201); } } ``` 1.10 JSON Web Tokens --------- If you already have your Quarkus project configured, you can add the smallrye-jwt extension to your project by running the following command in your project base directory: ```dockerfile= ./mvnw quarkus:add-extension -Dextensions="smallrye-jwt, smallrye-jwt-build" ``` application.properties: ```dockerfile= mp.jwt.verify.publickey.location=META-INF/resources/publicKey.pem mp.jwt.verify.issuer=http://htl.at/securitydemo smallrye.jwt.sign.key-location=privateKey.pem smallrye.jwt.new-token.lifespan=3600 smallrye.jwt.new-token.issuer=http://htl.at/securitydemo quarkus.smallrye-jwt.enabled=true ``` AuthService: ```java= package at.htl.services; import io.smallrye.jwt.build.Jwt; import io.vertx.core.json.JsonObject; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.jwt.Claims; import javax.inject.Inject; import javax.json.Json; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.time.Instant; import java.util.HashMap; import java.util.Map; @Path("api/auth") public class AuthService { @Inject UserRepository userRepository; @Inject @ConfigProperty(name = "smallrye.jwt.new-token.lifespan") long lifespan; @Inject @ConfigProperty(name = "smallrye.jwt.new-token.issuer") String issuer; @POST @PermitAll @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Response getToken(User user){ try { //Usually check in DB, if user is registered User u = userRepository.find("email", user.email).firstResult(); if (u == null || !user.passwort.equals(u.passwort)){ System.out.println(!user.passwort.equals(u.passwort)); return Response.status(Response.Status.UNAUTHORIZED).build(); } long exp = Instant.now().getEpochSecond() + lifespan; Map<String, Object> claims = new HashMap<>(); claims.put(Claims.upn.name(), v.email); claims.put(Claims.iss.name(), issuer); claims.put(Claims.exp.name(), exp); String token = Jwt.claims(claims).groups("user").sign(); String entity = Json.createObjectBuilder().add("token", token).add("expires_at", exp).add("id", v.id).build().toString(); return Response.ok().entity(entity).build(); } catch (Exception ex){ System.out.println(ex); return Response.status(Response.Status.UNAUTHORIZED).build(); } } } ``` Vor Funktionen oder Klassen kann mittels `@RolesAllowed("user")` geprüft werden ob der Benutzer auch wirklich für die angefragte Resource autorisiert ist. Adding publicKey.pem to resources/META-INF/resources ensures that it is available in the native image without having to provide a GraalVM resource file. RSA Public Key PEM Content: ``` -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEq Fyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwR TYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5e UF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9 AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYn sIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9x nQIDAQAB -----END PUBLIC KEY----- ``` Note for this code to work we need the content of the RSA private key that corresponds to the public key we have in the TokenSecuredResource application. Take the following PEM content and place it /resources/privateKey.pem: ``` -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCWK8UjyoHgPTLa PLQJ8SoXLLjpHSjtLxMqmzHnFscqhTVVaDpCRCb6e3Ii/WniQTWw8RA7vf4djz4H OzvlfBFNgvUGZHXDwnmGaNVaNzpHYFMEYBhE8VGGiveSkzqeLZI+Y02G6sQAfDtN qqzM/l5QX8X34oQFaTBW1r49nftvCpITiwJvWyhkWtXP9RP8sXi1im5Vi3dhupOh nelk5n0BfajUYIbfHA6ORzjHRbt7NtBl0L2J+0/FUdHyKs6KMlFGNw8O0Dq88qnM uXoLJiewhg9332W3DFMeOveel+//cvDnRsCRtPgd4sXFPHh+UShkso7+DRsChXa6 oGGQD3GdAgMBAAECggEAAjfTSZwMHwvIXIDZB+yP+pemg4ryt84iMlbofclQV8hv 6TsI4UGwcbKxFOM5VSYxbNOisb80qasb929gixsyBjsQ8284bhPJR7r0q8h1C+jY URA6S4pk8d/LmFakXwG9Tz6YPo3pJziuh48lzkFTk0xW2Dp4SLwtAptZY/+ZXyJ6 96QXDrZKSSM99Jh9s7a0ST66WoxSS0UC51ak+Keb0KJ1jz4bIJ2C3r4rYlSu4hHB Y73GfkWORtQuyUDa9yDOem0/z0nr6pp+pBSXPLHADsqvZiIhxD/O0Xk5I6/zVHB3 zuoQqLERk0WvA8FXz2o8AYwcQRY2g30eX9kU4uDQAQKBgQDmf7KGImUGitsEPepF KH5yLWYWqghHx6wfV+fdbBxoqn9WlwcQ7JbynIiVx8MX8/1lLCCe8v41ypu/eLtP iY1ev2IKdrUStvYRSsFigRkuPHUo1ajsGHQd+ucTDf58mn7kRLW1JGMeGxo/t32B m96Af6AiPWPEJuVfgGV0iwg+HQKBgQCmyPzL9M2rhYZn1AozRUguvlpmJHU2DpqS 34Q+7x2Ghf7MgBUhqE0t3FAOxEC7IYBwHmeYOvFR8ZkVRKNF4gbnF9RtLdz0DMEG 5qsMnvJUSQbNB1yVjUCnDAtElqiFRlQ/k0LgYkjKDY7LfciZl9uJRl0OSYeX/qG2 tRW09tOpgQKBgBSGkpM3RN/MRayfBtmZvYjVWh3yjkI2GbHA1jj1g6IebLB9SnfL WbXJErCj1U+wvoPf5hfBc7m+jRgD3Eo86YXibQyZfY5pFIh9q7Ll5CQl5hj4zc4Y b16sFR+xQ1Q9Pcd+BuBWmSz5JOE/qcF869dthgkGhnfVLt/OQzqZluZRAoGAXQ09 nT0TkmKIvlza5Af/YbTqEpq8mlBDhTYXPlWCD4+qvMWpBII1rSSBtftgcgca9XLB MXmRMbqtQeRtg4u7dishZVh1MeP7vbHsNLppUQT9Ol6lFPsd2xUpJDc6BkFat62d Xjr3iWNPC9E9nhPPdCNBv7reX7q81obpeXFMXgECgYEAmk2Qlus3OV0tfoNRqNpe Mb0teduf2+h3xaI1XDIzPVtZF35ELY/RkAHlmWRT4PCdR0zXDidE67L6XdJyecSt FdOUH8z5qUraVVebRFvJqf/oGsXc4+ex1ZKUTbY0wqY1y9E39yvB3MaTmZFuuqk8 f3cg+fr8aou7pr9SHhJlZCU= -----END PRIVATE KEY----- ``` 1.11 Sonstiges --------- ### Testdaten einfügen import.sql in Resources Ordner anlegen.\ Beispiele: ```sql= insert into courttype(id, description) values (1, 'Tennis'); insert into courttype(id, description) values (2, 'Squash'); insert into courttype(id, description) values (3, 'Badminton'); insert into court(id, location, courttype_id) values (1, 'Halle A', 1); insert into court(id, location, courttype_id) values (2, 'Halle B', 1); insert into court(id, location, courttype_id) values (3, 'Halle C', 1); ``` 2 Angular ======= 2.1 Testumgebung starten --- - Angular_Testumgebung.zip auf Desktop kopieren -> Dateien mit 7ZIP entpacken - In dem Ordner liegt das startAngular.bat File - startAngular.bat ausführen - Dadurch wird das globale NPM-Repository sowie die PATH-Variable neu gesetzt und ein Terminal Fenster geöffnet. - Alle weiteren Terminal-Windows ebenfalls mit diesem Script öffnen. - In dem geöffneten Fenster mit cd client auf das Projekt-Template wechseln mit ng serve --open kann man jetzt das Template starten - Nur das app Verzeichnis zum Schluss auf das U-Laufwerk kopieren vom Client. Den kompletten Server nach einem “Clear Project” auch auf das U-Laufwerk legen. 2.2 CLI --- - `npm install -g @angular/cli` - `ng new [project name]` - `ng serve --open` - `ng generate component [name]` - `ng generate service [name]` - `ng generate class [name]` - `ng add @angular/material` 2.3 Entity ------ Erstellung eines Typescript Interfaces. Erstelle einen Ordner namens “model” und erstelle darin deine Entities (.ts File) WICHTIG: Attribute am Client müssen wie am Server für Mapping benannt werden. ```typescript= export interface Reservation { firstname: String; lastname?: String; // optionale Property street: String; city: String; houseno: number; zip: String; } ``` Alternativ kann normale Typescript Klasse verwendet werden: ```typescript= export class Person { constructor( public id = 0, public vorname = '', public nachname = '', public email = '' ) { } } ``` 2.4 HTTP-Service ------------ Um die Daten vom Server anzufragen oder an den Server zu senden wird der HTTP-Service genutzt. Ein Service kann mit `ng generate service http` erstellt werden. ```typescript= import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { Customer } from '../model/customer'; @Injectable({ providedIn: 'root' }) export class HttpService { http: HttpClient; api: string = "http://localhost:8080"; constructor(http: HttpClient) { this.http = http; } } ``` ### GET <span>GET von mehreren Daten</span> ```typescript= getCustomerList(): Observable<Customer[]> { return this.http.get<Customer[]>(this.api + '/api/customer'); } ``` <span>GET von bestimmten Daten </span> ```typescript= getCustomer(id: number): Observable<Customer> { return this.http.get<Customer>(this.api + '/api/customer/' + id) } ``` ### POST ```typescript= addCustomer(customer: Customer): Observable<Customer> { return this.http.post<Customer>(this.api + '/api/customer', customer); } ``` ### PUT ```typescript= updateCustomer(customer: Customer): Observable<Customer> { return this.http.put<Customer>(this.api + '/api/customer', customer); } ``` ### DELETE ```typescript= deleteCustomer(id: number): Observable<Customer>{ return this.http.delete<Customer>(this.api + '/api/customer/' + id); } ``` 2.5 Data-Service ------------ An observable data service is an injectable service that can be used to provide data to multiple parts of the application. `ng generate service data` ```typescript= import { Injectable } from '@angular/core'; import { Ressource } from './models/ressource'; import { Person } from './models/person'; import { Todo } from './models/todo'; import { HttpService} from './http.service'; import { NbToastrService } from '@nebular/theme'; @Injectable({ providedIn: 'root' }) export class DataService { // for ts interfaces persons: Array<Person> = []; todos: Array<Todo> = []; ressourcen: Array<Ressource> = []; // for ts classes c_todos: Todo = new Todo(); c_persons: Person = new Person(); c_resources: Ressource = new Ressource(); constructor(public httpService: HttpService) { } findPerson(){ this.httpService.getAllPersons().subscribe(data => { this.persons = data; console.log(this.persons); }); } } ``` 2.6 Binding ------- ### Property Binding Durch Property-Binding können Properties von HTML-Elementen oder Komponenten gesetzt werden. ```htmlembedded= <div [style.visibility]="story ? 'visible' : 'hidden'" [class.special]="!isSpecial"> <img [src]="imagePath"> <a [href]="link">{{story}}</a> </div> <tr><td [attr.colspan]="1 + 1">One-Two</td></tr> ``` <span>**Input/Output:**</span> Da eine App oft aus hierarchisch geschachtelten Komponenten besteht, ist es auch möglich, Parameter von einer übergeordneten an eine untergeordnete Komponente mit Hilfe von Properties zu übergeben. ```typescript= // parent.html <person-detail [person]="person" (deleted)="deletePerson($event)"></person-detail> // child.ts @Input() person: Person; @Output() deleted = new EventEmitter<Person>(); onDelete() { this.deleted.emit(this.person); } // parent.ts deletePerson(person: event) { console.log('lastnameToDelete: '+person.lastname); } ``` ... dient dazu Daten zwischen Parent- und Child-Element auszutauschen ### Bidirectional Binding `<input type="text" [(ngModel)]="person.lastname"/>` **Wichtig:** NgModel benötigt das FormsModule: `import { FormsModule } from ’@angular/forms’;` ### Attribut Binding `<tr><td [attr.colspan]="1 + 1">One-Two</td></tr>` ### Class Binding ```htmlembedded= <div class="special" [class.special]="!isSpecial">This one is not so special</div> ``` ```htmlembedded= <div [ngStyle]="{'background-color':person.country === 'UK' ? 'green' : 'red' }"></<div> ``` ```htmlembedded= [ngClass]="{'text-success':person.country === 'UK'}" <some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element> ``` ### Event Binding ```htmlembedded= (selectionChange)="refreshAvailableTimeslots()" (click)="createReservation()" (dateChange)="refreshAvailableTimeslots()" ``` ### Style Binding `<button [style.backgroundColor]="canSave ?'cyan' : 'grey'" >Save</button>` ### Daten anzeigen **ngFor (For-Schleife):** `*ngFor="let item of itemArray; let i = index"` Wobei *item* jedes einzelne Element von *itemArray* wiederspiegelt. Der zweite Teil dient dazu einen Index (i) innerhalb des Elements zu verwenden. ***ngIf (If-Bedingung):** `*ngIf="item != undefined && item.length > 0"` Wenn die Bedingung *true* ist, dann wird das Element angezeigt. ***ngIf and Else** ```htmlembedded= <div *ngIf="isLoggedIn; else loggedOut"> Welcome back, friend. </div> <ng-template #loggedOut> Please friend, login. </ng-template> ``` ```htmlembedded= <ng-template [ngIf]="isLoggedIn" [ngIfElse]="loggedOut"> <div> Welcome back, friend. </div> </ng-template> <ng-template #loggedOut> <div> Please friend, login. </div> </ng-template> ``` 2.7 Pipes ----- ### Date ```javascript= <p>Today is {{ day | date }}</p> <p>Today is {{ day | date:"MM/dd/yy" }}</p> <p>Today is {{ day | | date: 'dd.MM.yyyy' }}</p> <p>Today is {{ day | date:format }}</p> <p>Today is {{ day | date | uppercase }}</p> <p>Today is {{ day | date | lowercase }}</p> <p>pi (3.5-5): {{pi | number:'3.5-5'}}</p> <!-- {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits} --> <pre>{{ object | json }}</pre> //Example: // .html {{slot.starttime | date: ’d.M.yyyy HH:mm:ss’}} // .ts formatDate(this.date, 'd.M.yyyy HH:mm:ss', this.locale) formatDate(this.date, 'yyyy-MM-dd HH:mm', this.locale) // constructor .. @Inject(LOCALE_ID) private locale: string, ... // imports import { formatDate } from '@angular/common'; import { LOCALE_ID } from '@angular/core'; // app.module.ts (mit Angular Material) import {MAT_DATE_LOCALE} from '@angular/material/core'; ... providers: [ { provide: MAT_DATE_LOCALE, useValue: 'de-DE' }, ], ``` ... dient dazu ein Date-Objekt zu formatieren. Obiges Beispiel formatiert Datum folgendermaßen: *14.12.2020 13:35:00* ### Currency `{{fuel.price |currency:'EUR':true}} ` 2.8 Angular Material Schematics --------------------------- `ng add @angular/material` In case you just want to install the @angular/cdk `ng add @angular/cdk` Address form schematic `ng generate @angular/material:address-form <component-name>` Navigation schematic `ng generate @angular/material:navigation <component-name>` Table schematic `ng generate @angular/material:table <component-name>` Dashboard schematic `ng generate @angular/material:dashboard <component-name>` Tree schematic `ng generate @angular/material:tree <component-name>` Drag and Drop schematic `ng generate @angular/cdk:drag-drop <component-name>` 2.9 Typescript Syntax ---------- ### Arrays ```typescript= let fruits: string[] = ['Apple', 'Orange', 'Banana']; let fruits: Array<string> = ['Apple', 'Orange', 'Banana']; ``` ```typescript= let userTestStatus: { id: number, name: string }[] = [ { "id": 0, "name": "Available" }, { "id": 1, "name": "Ready" }, { "id": 2, "name": "Started" } ]; userTestStatus[0].name; ``` ```typescript= var obj: MyGroupType = { "0": { "id": 0, "name": "Available" }, "1": { "id": 1, "name": "Ready" }, "2": { "id": 2, "name": "Started" } }; ``` ```typescript= private things: Thing[][]; constructor() { this.things = []; for(var i: number = 0; i < 10; i++) { this.things[i] = []; for(var j: number = 0; j< 10; j++) { this.things[i][j] = new Thing(); } } } ``` ```typescript= interface IStringList { [index:string]:string } let strArr : IStringList; strArr["TS"] = "TypeScript"; strArr["JS"] = "JavaScript"; ``` ```typescript= var arr_names:number[] = new Array(4) for(var i = 0;i<arr_names.length;i++) { arr_names[i] = i * 2 console.log(arr_names[i]) } ``` ### Promise ```typescript= getSpecificData(index: number){ let driversRequest = new Promise<number>((resolve) => { this.http.getDrivenKilometers(index).subscribe((resp) => { resolve(resp); }) }) return driversRequest; } setSpecificData(){ for (let index = 0; index < this.drivers.length; index++) { this.getSpecificData(this.drivers[index].id).then((data)=>{ if(data == undefined) { this.drivers[index].km = 0; } else{ this.drivers[index].km = data; } }); }); } ``` ### Sort Example ```typescript= sortByRidesByDate(){ this.ride.sort((a, b) => { if (a.date > b.date) return -1; if (a.date < b.date) return 1; return 0; }); } ``` ### Array-Funktionen ```typescript= [ 1, 2 ].concat([5], [7, 9]) // [ 1, 2, 5, 7, 9 ] [ 1, 2, 3, 4, 5 ].copyWithin(0,2) // [ 3, 4, 5, 4, 5 ] [1, 30, 40].every(val => val > 0) // true [1, 2, 3, 4].fill('x', 1, 3) // [ 1, "x", "x", 4 ] [1, 10, 5, 6].filter(val => val > 5) // [ 10, 6 ] [1, 10, 5, 6].find(val => val > 5) // 10 [1, 4, 5, 6].findIndex(val => val > 5) // 3 [1, [2, [3, [4]]]].flat(2) // [ 1, 2, 3, [4] ] [[2], [4], [6], [8]].flatMap(val => val/2) // [ 1, 2, 3, 4 ] [ 1, 2, 3 ].forEach(val => console.log(val)) // 1 // 2 // 3 [ 1, 2, 3 ].includes(3) // true [ 1, 2, 3 ].indexOf(3) // 2 [ "x", "y", "z" ].join(" - ") // "x - y - z" [ 1, 2, 3, 1, 0].lastIndexOf(1) // 3 [ 2, 3, 4 ].map(val => val * 2) // [ 4, 6, 8 ] const arr = [ 1, 2, 3 ] arr.pop() // returns: 3 // arr is [ 1, 2 ] const arr = [ 1, 2, 3 ] arr.push(1) // returns: 4 // arr is [ 1, 2, 3, 4 ] [ 'a', 'b', 'c' ].reduce((acc, curr) => acc + curr, 'd') // "dabc" [ 'a', 'b', 'c' ].reduceRight((acc, curr) => acc + curr, 'd') // "dcba" [ 1, 2, 3 ].reverse() // [ 3, 2, 1 ] const arr = [ 1, 2, 3 ] arr.shift() // returns: 1 // arr is [ 2, 3 ] [ 1, 2, 3, 4 ].slice(1, 3) // [ 2, 3 ] [ 1, 2, 3, 4 ].some(val => val > 3) // true [ 1, 2, 3, 4 ].sort((a, b) => b - a) // [ 4, 3, 2, 1 ] const arr = [ 1, 2, 3, 4 ] arr.splice(1, 2, 'a') // returns [ 2, 3 ] // arr is [ 1, "a", 4 ] [1.1, 'a', new Date()].toLocaleString('EN') // "1.1,a,5/18/2020, 7:58:57 AM" Object.keys(fruits); // ['0', '1', '2', '5'] const arr = [ 1, 2, 3 ] arr.unshift(0, 99) // returns 5 // arr is [ 0, 99, 1, 2, 3 ] ``` ### Date ```javascript= let today = new Date(); let d = today.getDate(); new Date('Mar 25 10:30:00 2020'); new Date('1995-12-17T03:24:00'); new Date('1995-12-17'); const formattedDate = formatDate(new Date(), 'dd.MM.yyyy', 'en-US'); ``` 2.10 Routing ------- In Angular wird zwischen Componenten gewechselt mittels Routing. Hierzu gibt es das app-routing.module.ts. Die Routes werden als Array hier eingetragen. ```typescript= import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { CustomerComponent } from './customer/customer.component'; import { NewReservationComponent } from './new-reservation/new-reservation.component'; import { ReservationListComponent } from './reservation-list/reservation-list.component'; const routes: Routes = [ {path: 'customer', component: CustomerComponent}, {path: 'resList', component: ReservationListComponent}, {path: 'resNew', component: NewReservationComponent}, //wildcard routes { path: '', component: CustomerComponent, pathMatch: 'full'}, { path: '**', redirectTo: 'customer' }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { } ``` Auf den jeweiligen HTTP-Seiten auf der man Routing benutzen möchte muss man nur folgende Code-Zeile verwenden. `<a routerLink="/resList" mat-list-item href="#">Reservierungsliste</a>` Die Startseite (app.component.html) soll mit `<router-outlet></router-outlet>` gekenntzeichnet werden. Dort werden dann die unterschiedlichen Routes hineingeladen, die gerade angesprochen wurden. Routing in Typescript: `import { Router } from '@angular/router';` `constructor(public router: Router) {}` `this.router.navigate(['/login']);` 2.11 App-Module ---------- Imports überprüfen! Services bei Providers eintragen. ```typescript= import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ReactiveFormsModule } from '@angular/forms'; import { HttpClientJsonpModule, HttpClientModule } from '@angular/common/http'; import { FormsModule } from '@angular/forms'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule, FormsModule, BrowserAnimationsModule, ReactiveFormsModule, HttpClientJsonpModule, HttpClientModule ], providers: [HttpService, DataService], bootstrap: [AppComponent] }) export class AppModule { } ``` 2.12 Websockets ---------- `ng g service websocket` ```typescript= import { Injectable } from '@angular/core'; import { webSocket, WebSocketSubject } from 'rxjs/webSocket'; @Injectable({ providedIn: 'root' }) export class WebsocketService { connection: WebSocketSubject<any>; username = new Date().getMilliseconds()+Math.random()*100; message: string; url="ws://localhost:8080/chat" constructor() { } connect(): void { this.connection = webSocket({ url: this.url + '/' + this.username, deserializer: msg => msg.data }); this.connection.subscribe( value => console.log(value), error => console.log('Error: ' + error), () => console.log('Disconnected...') ); } send(): void { this.connection.next(this.message); } disconnect(): void { if (this.connection) { this.connection.complete(); this.connection = null; } } } ``` Treffen neue Nachrichten ein, können beispielweise alle Daten neu geladen werden: ```typescript= this.connection = webSocket({ url: this.url + '/' + this.username, deserializer: msg => msg.data }); this.connection.subscribe((value) =>{ this.findDrivers(); this.findFuels(); this.findRepairs(); this.findRide(); } ); ``` 2.13 SSE --- ```typescript= import { NgZone } from '@angular/core'; import { Component, OnInit } from '@angular/core'; import { Observable, Subscription } from 'rxjs'; @Component({ selector: 'app-sse', templateUrl: './sse.component.html', styleUrls: ['./sse.component.css'] }) export class SseComponent implements OnInit { info = 'no info'; sse: Subscription; constructor(private zone: NgZone) { } ngOnInit(): void { this.sse = this.getSseObservable().subscribe( value => this.info = value, error => console.log(error) ); } public getSseObservable(): Observable<any> { return new Observable<any>(subscriber => { const eventSource = new EventSource('http://localhost:8080/sse/subscribe'); eventSource.onmessage = event => { this.zone.run(() => { subscriber.next(event.data); }); }; eventSource.onerror = event => { this.zone.run(() => { subscriber.error(event); }); }; }); } } ``` 2.14 Authentication (JWT) ---------- Nachdem das Token vom erfolgreichen Login-Aufruf erhalten wurde wird es meist im LocalStorage abgelegt. Dieses Token muss dann bei allen Requests im Header mitgesendet werden, wofür sich ein HttpInterceptor anbietet: Ein neuer Interceptor kann mit `ng generate interceptor <name>` erstellt werden. ```typescript= import { Injectable } from '@angular/core'; import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable() export class AuthInterceptor implements HttpInterceptor { intercept( req: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> { const idToken = localStorage.getItem('id_token'); if (idToken) { const cloned = req.clone({ headers: req.headers.set('Authorization', 'Bearer ' + idToken), }); return next.handle(cloned); } else { return next.handle(req); } } } ``` Zum Aktivieren wird der in der Datei `app.modules.ts` im Abschnitt `providers` angegeben. ``` providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, ], ``` Eine entsprechende Login Komponente könnte dann folgendermaßen aussehen: ```typescript= public login(username: string, password: string): void { this.http.login(username, password).subscribe( (data) => { localStorage.setItem('id_token', data.token); localStorage.setItem('user_id', data.id.toString()); // this.data.customerid = data.id; this.router.navigate(['/']); }, (err: Error) => { alert('Login failed'); } ); } ``` ### Auth Guards Zusätzlich kann ein AuthGuard implementiert werden. Darin könnten wir beispielsweise überprüfen, ob der Expires Zeitstempel schon überschritten wurde. Wenn ja leiten wir auf die Login-Maske um: ```typescript= { path: 'customer', component: CustomerComponent, canActivate: [AuthGuardService] }, ``` AuthGuard-Implementierung: ```typescript= import { Injectable } from '@angular/core'; import { CanActivate, Router } from '@angular/router'; @Injectable({ providedIn: 'root', }) export class AuthGuardService implements CanActivate { constructor(private router: Router) {} canActivate(): boolean { if (localStorage.getItem('id_token') == null) { this.router.navigate(['/login']); return false; } return true; } } ``` 2.15 HTML Cheatsheet ---------- Table: ```htmlembedded= <table><caption>Phone numbers</caption> <thead> <tr> <th>Name</th> <th colspan="2">Phone</th> </tr> </thead> <tbody> <tr> <td>John</td> <td>577854</td> <td>577855</td> </tr> </tbody> <tfoot> <tr> <td>&nbsp;</td> <td>Personal</td> <td>Office</td> </tr> </tfoot> </table> ``` Button: ```htmlembedded= <input type="button" (click)="onSave()" value="Klick mich">` ``` Input: ```htmlembedded= <input [value]="foo" (change)="changeFn($event)"> <input [ngModel]="bar" (ngModelChange)="modelChangeFn($event)"> ``` Radio-Button: ```htmlembedded= <input id="male" type="radio" value="male" name="gender" formControlName="gender" (change)="changeGender($event)"> <label for="male">Male</label> <input type="radio" name="{{groupName}}" [value]="choice" [(ngModel)]="defaultChoice" (ngModelChange)="choose($event)" /> ``` Selectbox: ```htmlembedded= <select [(ngModel)]="data" (ngModelChange)="dataChanged($event)" name="data"> <option *ngFor="let currentData of allData" [ngValue]="currentData"> {{data.name}} </option> </select> <select (change)="changed($event)"> <option *ngFor="let currentData of allData" [value]="currentData.id"> {{data.name}} </option> </select> ``` Textarea: ```htmlembedded= <textarea id="subject" rows="4" cols="40"> ... </textarea> ``` Checkbox: ```htmlembedded= <input (change)="fieldsChange($event)" [value]="currentValue" type="checkbox"/> //.ts file fieldsChange(values:any):void { console.log(values.currentTarget.checked); } <input type="checkbox" [checked]="saveUsername" (change)="saveUsername = !saveUsername"/> <input type="checkbox" id="rememberMe" name="rememberMe" [(ngModel)]="rememberMe"> ``` List: ```htmlembedded= <ul> <li>First</li> <li>Second</li> <li>Third</li> </ul> <dl> <dt>HTML</dt> <dd>Hypertext Markup Language</dd> <dt>CSS</dt> <dd>Cascading Style Sheets </dd> </dl> ``` Horizontal Line: `<hr /> ` Date: ```htmlembedded= <label for="start">Start date:</label> <input type="date" id="start" name="trip-start" value="2018-07-22" min="2018-01-01" max="2018-12-31"> ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully