Jerry's Blog

Recording what I learned everyday

View on GitHub


8 July 2019

Angular (24) -- Authentication

by Jerry Zhang

LeetCode Day 3: P66. Plus One

题目

一个非空数组,用来表示一个非负的整数,比如123,就用[1, 2, 3]来表示。

求这个整数加1后的数组表示。

Input: [4,3,2,1]

Output: [4,3,2,2]

Explanation: The array represents the integer 4321.

我的思路:

就按小学数学的思路做,从最后一个数字开始循环,记录有没有进位。

我的算法:

  1. 先把最后一个数字加一, 看是不是变成了10。如果是,就要进位,把carry变成true。

  2. 如果原数组长度大于1,就从倒数第二个数开始往前循环,看是否有遗留的进位。有进位的话,当前数字就加一。加完后再判断以后是否需要进位, 就通过看加完一后,这个数字是否等于10就可以了。如果不需要进位,记得把carry改回false。

  3. 遍历完整个数组,再查一次是否需要进位。此时如果需要进位,说明最高位需要进位,那整个数组长度就加了一,于是就不得不新建一个数组, 以一开头,然后复制原数组。

我的代码:

public class E_66_PlusOne {
    public static int[] plusOne(int[] digits) {
        int length = digits.length;
        boolean carry = false;
        if (++digits[length - 1] == 10) {
            digits[length - 1] = 0;
            carry = true;
        }
        if (length > 1) {
            for (int i = length - 2; i >= 0; i--) {
                if (carry) {
                    digits[i]++;
                }
                if (digits[i] == 10) {
                    digits[i] = 0;
                    carry = true;
                } else {
                    carry = false;
                }
            }
        }
        if (carry) {
            int[] newInteger = new int[digits.length + 1];
            newInteger[0] = 1;
            for (int i = 1; i < newInteger.length; i++) {
                newInteger[i] = digits[i - 1];
            }
            return newInteger;
        }
        return digits;
    }

    public static void main(String[] args) {
        int[] digits = {9,9,9};
        int[] ints = plusOne(digits);
        System.out.println("ints = " + Arrays.toString(ints));
    }
}

最优解法:

class Solution {
    public int[] plusOne(int[] digits) {
        int index = digits.length - 1;
        while(index >= 0 && digits[index] == 9) {
            digits[index] = 0;
            index--;
        } 
        if(index == -1) {
            int[] array = new int[digits.length + 1];
            array[0] = 1;
            return array;
        }
        digits[index]++;
        return digits;
    }
}

反思

其实不需要先加1,(最后再加),只要判断最后一个数字是不是9就行了。如果是9,就把这个数字改成0,然后往前看,直到不是9为止。

这么做是因为,加完1后使其他数位改变的唯一可能就是以“999…”结尾。不是9的数位加完1后肯定不会影响其他数位。如果查完9发现index变成了 -1,说明所有数字都是9,就要新建一个数组。最后要把当前index的数字加1。

Angular

Adding the Token to Outgoing Requests

We know the user is authenticated, but firebase does not know. We have to attach this token in the Http request header or as a parameter.

To achieve this, we could save the user token in the auth service class, but here we can also use another rxjs Subject, BehaviorSubject. BehaviorSubject also gives subscribers immediate access to the previously emitted value even if they haven’t subscribed at the point of time that value was emitted.

So, when we fetch data, the user had logged in earlier, but still we can get the user from BehaviorSubject.

user = new BehaviorSubject<User>(null);

BehaviorSubject needs to be initialized with a starting value, which is null in this case.

Now we can reach out to the auth service user to get the currently active user when we fetch data.

We only want to get the user once, so we don’t want to use the subscribe. We can use pipe and take the value once instead.

take operator is a function that your simply pass a number to it, for example one here, and means that we want to take this number of value from the observable and thereafter it should automatically unsubscribe.

fetchRecipes() {
    return this.authService.user.pipe(
      take(1),
      exhaustMap(user => {
        return this.http.get<Recipe[]>(
          'https://recipebook-cc2b1.firebaseio.com/recipes.json',
          {
            params: new HttpParams().set('auth', user.token)
          }
        );
      }),
      map(recipes => {
        return recipes.map(recipe => {
          return {
            ...recipe,
            ingredients: recipe.ingredients ? recipe.ingredients : []
          };
        });
      }),
      tap(recipes => {
        this.recipeService.setRecipes(recipes);
      })
    );
  }

exhaustMap waits for the first Observable to complete, which happened after we took the latest user. Thereafter, it gives us that user. In the exhaustMap, we return a new observable and replace our previous observable in the entire observable chain. So we start with a user observable and once we are done, this will be replaced by a http observable. At last, we return this overall observable.

Attaching the Token with an Interceptor

Because we want to attach this token in all the outgoing httpRequests, we can use an interceptor.

Create an interceptor service file.

@Injectable()
export class AuthInterceptorService implements HttpInterceptor {

  constructor(private authService: AuthService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.authService.user.pipe(
      take(1),
      exhaustMap(user => {
        if (!user) {
          return next.handle(req);
        }
        const modifiedReq = req.clone({params: new HttpParams().set('auth', user.token)});
        return next.handle(modifiedReq);
      }));
  }
}

In the app.module file, we need a different way to tell Angular this is an interceptor, instead of a normal service class.

providers: [ShoppingListService, RecipeService, {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptorService, multi: true}],

Log out

Set the user to null.

logout() {
    this.user.next(null);
  }

In the header component, create a click event and call the logout method there.

onLogout() {
    this.authService.logout();
}

Because there are multiple places lead us log out, we add the redirect logic in the service.

So we first inject the router in the auth service.

constructor(private http: HttpClient, private router: Router) {}

Then navigate to another page when we log out.

logout() {
    this.user.next(null);
    this.router.navigate(['/auth']);
  }
tags: Angular