The below code snippet we can view the result of using .map versus .switchMap
//user.service.ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from "rxjs";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
@Injectable()
export class UserService {
constructor(private http: Http) { }
getUsers(): Observable<any> {
return this.http.get('http://jsonplaceholder.typicode.com/users')
//.map(v => v.json());
.switchMap(v => v.json());
}
}
//app.component.ts
import { Component } from '@angular/core';
import { UserService } from "./user.service";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
constructor(private userService: UserService) {
}
search(term: string) {
this.userService.getUsers()
.subscribe(v => console.log(v));
/*
//we can do this with .switchMap
this.userService.getUsers()
.subscribe(v => {if (v.email != "Sincere@april.biz") {
console.log(v.email);
}});
*/
}
}
Solving the multiple Async Pipe in Angular ≥ 2.0.0 with share operator
Remember to import import "rxjs/add/operator/share"; See reference here.
squareData$: Observable<string> = Observable.range(0, 10)
.map(x => x * x)
.do(x => console.log(`CalculationResult: ${x}`)
.toArray()
.map(squares => squares.join(", "))
.share(); // remove this line: console will log every result 3 times instead of 1
Managing Cold and Hot Observables using publish().refCount() which is similar to .share()
ngOnInit() {
// in angular 2 and above component.ts file add these this.coldObservable();
this.hotObservable();
}
/*
* cold observable is like a recast of video
* */
coldObservable() {
let incrementalObs = Observable.interval(1000).take(10).map(x => x + 1);
incrementalObs.subscribe(val => console.log('a: ' + val));
setTimeout(function() {
incrementalObs.subscribe(val => console.log(' b: ' + val));
}, 4500);
}
/*
* hot observable is like watching a live video
* */
hotObservable() {
let incrementalObs = Observable.interval(1000).take(10).map(x => x + 1).publish().refCount(); //can also use .share()
incrementalObs.subscribe(val => console.log('a: ' + val));
setTimeout(function() {
incrementalObs.subscribe(val => console.log(' b: ' + val));
}, 4500);
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
title = 'app works!';
obs = Observable.of(1, 2, 3, 4);
ngOnInit() {
this.howToHandleErrorV1();
this.howToHandleErrorV2();
this.howToUseRetry();
this.mergeObservableAndThrowError();
this.mergeObservableAndErrorResumeNext();
this.mergeObservableAndErrorCatch();
}
/*
* This uses Catch for V1. This introduces Closure. It is effectively the same as V2.
* */
howToHandleErrorV1() {
this.obs
.map(x => {
if ( x === 3 ) {
throw 'I hate threes'; // When it hitted error it actually unsubscribe itself at x === 3 of throw error
}
return x;
})
.catch(err => Observable.throw('Caught error here Observable.throw')) // continue go down the error path use Observable.throw
.catch(err => Observable.of('Caught error here Observable.of')) // catch just use Observable.of
.subscribe(
x => console.log(x),
err => console.error(err), // If not catch any where, the I hate threes errors will be propagated to here
() => console.log('done completed')
);
}
/*
* There is a difference between V1 and V2. For V2 it is using onErrorResumeNext which
* */
howToHandleErrorV2() {
let good = Observable.of('Caught error here Observable.of');
this.obs
.map(x => {
if ( x === 3 ) {
throw 'I hate threes'; // When it hit error it actually unsubscribe itself at x === 3 of throw error
}
return x;
})
.onErrorResumeNext(good) // To catch just use Observable.of
.subscribe(
x => console.log(x),
err => console.error(err), // If not catch any where, the I hate threes errors will be propagated to here
() => console.log('done completed')
);
}
/*
* For this we use see it retries three times then console.error(err);
* So retryWhen is for trying network connection websocket
* */
howToUseRetry() {
this.obs
.map(x => {
if ( x === 3 ) {
throw 'I hate threes'; // When it hitted error it actually unsubscribe itself at x === 3 of throw error
}
return x;
})
.retry(3) // retry three times
.retryWhen(err => err.delay(2000).take(3)) // similar but with 2 seconds delay and the error is not propagated.
.retryWhen(err => err.delay(2000).take(3).concat(Observable.throw('bad'))) // this it would throw an error.
.subscribe(
x => console.log(x),
err => console.error(err), // If not catch any where, the I hate threes errors will be propagated to here
() => console.log('done completed')
);
}
/*
* Using observable merge operator
* */
mergeObservableAndThrowError() {
let mergedObs = Observable.merge(
this.obs, //1, 2, 3, 4
Observable.throw('Stop Error'),
Observable.from(this.array), //0, 1, 2, 3, 4, 5
Observable.of(999) //999,
);
mergedObs.subscribe(
val => console.log(val), //this should show 1, 2, 3, 4, Stop Error
error => console.log(error),
() => console.log("completed")
);
}
/* Using observable onErrorResumeNext just like merge operator
* */
mergeObservableAndErrorResumeNext() {
let mergedObs = Observable.onErrorResumeNext(
this.obs, //1, 2, 3, 4
Observable.throw('Stop Error'),
Observable.from(this.array), //0, 1, 2, 3, 4, 5
Observable.of(999) //999,
);
mergedObs.subscribe(
val => console.log(val), //this should show 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 999
error => console.log(error),
() => console.log("completed")
);
}
/*
* Using observable merge operator and catch
* */
mergeObservableAndErrorCatch() {
let mergedObs = Observable.merge(
this.obs, //1, 2, 3, 4
Observable.throw('Stop Error'),
Observable.from(this.array), //0, 1, 2, 3, 4, 5
Observable.of(999) //999,
).catch(e => {
console.log(e);
return Observable.of('catch error here');
});
mergedObs.subscribe(
val => console.log(val), //this should show 1, 2, 3, 4, Stop Error, Catch Error Here
error => console.log(error),
() => console.log("completed")
);
}
}
map vs flatMap in RxJS
Transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable. See my GitHub repo.
obs = Observable.of(1, 2, 3, 4);
ngOnInit() {
this.usingMap();
this.usingMapToMakeInnerObservable();
this.usingMapAndMergeAll();
this.usingFlatMap();
}usingMap() {
this.obs
.map(x => x * 2) // transform the input by multiple of 2
.subscribe(
x => console.log(x),
err => console.error(err),
() => console.log('done completed')
);
}
usingMapToMakeInnerObservable() {
this.obs
.map(x => Observable.timer(500).map(() => x + 3)) // transform the input wrapping it with another observable and addition of 3
//.map(x => Observable.timer(500).map((x) => x + 3)) // !!! REMEMBER Not the same as the immediate above
.subscribe(
x => console.log(x),
err => console.error(err),
() => console.log('done completed')
);
}
// Map and Merge all is the same as just one FlatMap
usingMapAndMergeAll() {
this.obs
.map(x => Observable.timer(500).map(() => x + 3)) // transform the input wrapping it with another observable and addition of 3
.mergeAll()
.subscribe(
x => console.log(x),
err => console.error(err),
() => console.log('done completed')
);
}
// Flat map is the same as map then merge all
// transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable
usingFlatMap() {
this.obs
.flatMap(x => Observable.timer(500).map(() => x + 10)) // transform the input wrapping it with another observable and addition of 10
.subscribe(
x => console.log(x),
err => console.error(err),
() => console.log('done completed')
);
}
Transforming pure Javascript array vs. Observable from array
array = [0, 1, 2, 3, 4, 5];
ngOnInit() {
this.setArrayToObservableThenTransform();
}/*
* This keeps creating new array. It is good that it creates new array of arr for immutability.
* But it's bad because there is clean up and resource intensive for mobile
* */
transformArray() {
let result = this.array
.filter(( x, i, arr ) => {
console.log('filtering ' + x);
console.log('is the source array ' + (arr === this.array));
return x
})
.map(( x, i, arr ) => {
console.log('mapping ' + x);
console.log('is the source array ' + (arr === this.array));
return x + '!';
})
.reduce(( r, x, i, arr ) => {
console.log('reducing ' + x);
return r + x;
}, '--');
console.log(result);
}
/*
* This is more efficient for resource management because it linearly scans and discard when not right
* */
setArrayToObservableThenTransform() { let obsArray = Observable.from(this.array); // Use Observable.from() instead of Observable.of(). There is diff. obsArray
.filter(( x: any ) => {
console.log('filtering ' + x);
return x
})
.map(( x ) => {
console.log('mapping ' + x);
return x + '!';
})
.reduce(( r, x ) => {
console.log('reducing ' + x);
return r + x;
}, '--')
.subscribe(
x => console.log(x)
);
}
array = [0, 1, 2, 3, 4, 5];ngOnInit() {
this.reduceArray();
this.reduceObservableArray();
this.reduceObservableArray_Abstract2();
this.scanObservableArray();
}
/*
* This is the same as reduceObservableArray()
* */
reduceArray() {
let result = this.array.reduce(
(accumulator, currentValue) => accumulator + currentValue, 3
); // 3 is the init value.
console.log('reduceArray ' + result); // output 18 => 3 + (0 ... 5)
}
/*
* This is the same as reduceArray()
* But this waits for all the arrays to finish emitting before reducing them to one single number
* See the next method to understand better
* */
reduceObservableArray() {
let obsArray = Observable.from(this.array);
obsArray.reduce(
(accumulator, currentValue) => accumulator + currentValue, 3
).subscribe(
val => console.log('reduceObservableArray ' + val)
);
}
/*
* The exact same reduce function/method as of reduceObserableArray() above
* This proves that it waits for all 6 numbers to come in then reduce them
* */
reduceObservableArray_Abstract2() {
let obsArray = Observable.interval(1000).take(6); //emits 6 times of 0, 1, 2, 3, 4, 5
obsArray.reduce(
(accumulator, currentValue) => accumulator + currentValue, 3
).subscribe(
val => console.log('reduceObservableArray_Abstract2 ' + val)
);
}
/*
* This is the same as the above reduceObserableArray_Abstract2()
* except this is using scan instead of reduce
* */
scanObservableArray() {
let obsArray = Observable.interval(1000).take(6); //emits 6 times of 0, 1, 2, 3, 4, 5
obsArray.scan(
(accumulator, currentValue) => accumulator + currentValue, 3
).subscribe(
val => console.log('scanObservableArray() ' + val)
);
}
Create, next, and subscribe to Subject and BehaviorSubject
There is a Stack Overflow thread which discussed about the difference between Subject and BehaviorSubject. It’s worth understanding.
import { Subject } from 'rxjs/Subject';
import { BehaviorSubject } from "rxjs/BehaviorSubject";
// create subject
// there is no need for initial value
subject = new Subject<boolean>();
// create behaviorSubject which require initial value
// true is an initial value. if there is a subscription
// after this, it would get true value immediately
behaviorSubject = new BehaviorSubject<boolean>(true);
ngOnInit() {
this.subject.next(false); /* Subject subscription wont get anything at this point before the subscribeSubject() */ this.subscribeSubject();
this.subscribeBehaviorSubject();
}
/*
* Push the next val into the behavior subject
* */
nextSubject(val: boolean) {
this.subject.next(val);
}
/*
* Any values push into the subject would not be can shown
* before this subscribeSubject() is called
* */
subscribeSubject() {
this.subject
//.take(1) //when we include .take(1) we will have a complete. Without this it will continue subscribing
.subscribe(
val => console.log(val),
err => console.error(err),
() => console.log('completed')
);
}
/*
* This is the proper way to return a subject as observable
* */
getSubject(): Observable<boolean> {
return this.subject.asObservable();
}
/*
* Push the next val into the behavior subject
* */
nextBehaviorSubject(val: boolean) {
this.behaviorSubject.next(val);
}
/*
* For angular Behavior subject for a data service as a angular service often initializes
* before component and behavior subject ensures that the component consuming the
* service receives the last updated data even if there are no new
* updates since the component's subscription to this data.
* */
subscribeBehaviorSubject() {
this.behaviorSubject
// .first()
.subscribe(
val => console.log(val),
err => console.error(err),
() => console.log('completed')
);
}
usingFinallyOperator() {
Observable
.interval(500)
.take(4)
.finally(() => console.log('End of the observable, Hello World'))
.subscribe(
val => console.log('count taker ' + val)
);
}
Stopping / Intercepting Observable
Imagine using Gmail where it allows you to undo email sent? We can produce similar experience with Observable
// subscription is created when an observable is being subscribed
subscription: Subscription;
// boolean variable for showing stop observable using takeWhile operator
isTrue: boolean = true;
/*
* basic interval can be used as delay too
* Imagine Gmail allows you to send and undo send within 4 seconds of sending
* Use Case: Perform an action 8 seconds later then intercept if user choose to undo the action
* */basicInterval() {
let undoInSeconds: number = 8;
this.subscription = Observable
.interval(1000)
.take(undoInSeconds)
.takeWhile(() => this.isTrue)
.subscribe(
(val: number) => {
console.log(`${val + 1} seconds... UNDO`);
( val === (undoInSeconds - 1) ) ? console.log('Email sent / Action performed') : null;
}
);
}
/*
* This is to stop observable from continuing performance
* Use Case: Stop observable from running like how Gmail could undo email being sent
* */stopObservableUsingUnsubscribe() {
if (!!this.subscription) {
this.subscription.unsubscribe();
console.log('subscription: Subscription is unsubscribed');
}
}
/*
* This is also to stop observable from continuing performance
* This method is more preferable than subscribing method then unsubscribe
* Use Case: Stop observable from running like how Gmail could undo email being sent
* */stopObservableUsingTakeWhile() {
this.isTrue = false;
}
Perform conditional Reactive Form validation
This is my approach to performing conditional validation when using Angular. We will minimally manipulate the Observable of RxJS in this example. Let’s try by creating or using app.component.ts.
1 . Create a form that has two form controls reason and otherReason.
/* * Refer to angular official guide at https://angular.io/guide/reactive-forms on how to create reactive form with form controls * */createForm() {
this.form = this.formBuilder.group({ reason: ['', Validators.required ],
otherReason: [''], });
}
2 . Create two methods addValidator and removeValidator.
/* * For conditional form validation use * */
private addValidator( control: AbstractControl, newValidator ){
let existingValidators = control.validator;
control.setValidators(Validators.compose([ existingValidators, newValidator ]));
control.updateValueAndValidity();
}
/* * For conditional form validation use * */
private removeValidator( control: AbstractControl ){
control.clearValidators();
control.updateValueAndValidity();
}
3 . Create third method called conditionalFormValidation.
The outcome should illustrate that if we select ‘Others’ option in the dropdown list of reason, it should make otherReason form control field as required.
Perform manual operations after reading from Firebase database
When using AngularFire2 with Angular + Firebase, in getting a list of data from Firebase, we will get one observable instance but within that one observable is an array of N size. We can manually filter the array inside that one observable instance using arr.filter. It is different from RxJS .filter operator. Of course we can also flatten what is inside an array using .flatMap() operator. However, we’re going use JavaScript array filtering, instead of non-observable filtering, right after getting an observable object.
We can also reverse an array using JavaScript array reverse function. See reference. On a side note, using negative timestamp to reverse Firebase display is also another option.
Besides those above, we can also use the response returned from AngularFire2 to perform “local filter/search”. This result can be valuable for autocomplete filtered list or searches. However, this approach below suffers severely in performance issue where at the magnitude of the size of the returned response from AngularFire2. E.g. if the list has N items. It has to iterate at least 1N. Perhaps an average of 2N.
The task of creating an error free program is not easy. And, if your program runs free of errors, keeping it error-free after an update or change is even more complicated. You don’t want to insert new errors or change correct code with wrong parts.
The answer to this situation (directly from the Oracle of Delphi) is: Testing, Testing, Testing
And the best way to test is to start with tests.
This means: think about what the result should be and then create a Test that checks this. Imagine, you have to write a function for adding two values, and you should describe the functionality.
So, maybe, your description contains one or two examples:
My functions add’s two numbers, e.g 5 plus 7 is 12 (or at least should be 12 :))
The procedure with the TDD is:
think and define, what the function should to
write a stub for the function, e.g. only function parameters and return type
write a function, that tests you function with defines parameters and know result
For our example above, this means:
Write the python script with the desired functionality: src/main.py
def add(val1,val2):
return 0 # this is only a dummy return value
Write the Python Testscript: tst/main.p
def_test_add():
result = add(5,7)
if (result = 12):
print("everything fine")
else:
printf("ups, problems with base arithmetics")
Now, with these in your toolbox, you can always verify your code by running the tests.
$ python test_add.py
ups, problems with base arithmetics
dfdf
Setup virtual environment
Mostly, tests are repeated after every change. So, to be sure, that each test is running the same way and with the same environment, we will use pythons virtual environment feature to create a new fresh python environment for the tests.
Create virtual environment
$ python3 -m venv .env/python
Activate environment
Add the following line to .bashrc (or .envrc if you are using direnv)
At least, create a simple Calculator: src/CalculatorLib/Calculator.py
class Calculator:
def __init__(self):
print("Init Calculator")
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
def multiply(self, a, b):
return a * b
def divide(self, a, b):
return a / b
def power(self, base, exp):
return base ** exp
Create the Main App for your Calculator: src/main.py
from CalculatorLib.Calculator import Calculator
class Main(object):
def run(self):
c = Calculator()
print("5 + 3 =
print("8 - 4 =
print("5 * 3 =
print("8 / 4 =
print("8 ^ 4 =
if __name__ == '__main__':
Main().run()
Yur done with the fist development step. Try your app:
Now, implement the function correctly and startover the test:
Add a function at the end of your Calculator: src/CalculatorLib/Calculator.py
import math
class Calculator:
...
def factorial(self, n):
if not n >= 0:
raise ValueError("n must be >= 0")
if math.floor(n) != n:
raise ValueError("n must be exact integer")
if n+1 == n: # catch a value like 1e300
raise OverflowError("n too large")
result, factor = 1, 2
while factor <= n:
result *= factor
factor += 1
return result
from radish import given, when, then
@given("I have the numbers {number1:g} and {number2:g}")
def have_numbers(step, number1, number2):
step.context.number1 = number1
step.context.number2 = number2
@when("I sum them")
def sum_numbers(step):
step.context.result = step.context.number1 + \
step.context.number2
@then("I expect the result to be {result:g}")
def expect_result(step, result):
assert step.context.result == result
"""
The example module supplies one function, factorial(). For example,
>>> factorial(5)
120
"""
def factorial(n):
"""Return the factorial of n, an exact integer >= 0.
>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
>>> factorial(30)
265252859812191058636308480000000
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n must be >= 0
Factorials of floats are OK, but the float must be an exact integer:
>>> factorial(30.1)
Traceback (most recent call last):
...
ValueError: n must be exact integer
>>> factorial(30.0)
265252859812191058636308480000000
It must also not be ridiculously large:
>>> factorial(1e100)
Traceback (most recent call last):
...
OverflowError: n too large
"""
import math
if not n >= 0:
raise ValueError("n must be >= 0")
if math.floor(n) != n:
raise ValueError("n must be exact integer")
if n+1 == n: # catch a value like 1e300
raise OverflowError("n too large")
result = 1
factor = 2
while factor <= n:
result *= factor
factor += 1
return result
if __name__ == "__main__":
import doctest
doctest.testmod()
$ nosetests -v
test_base.test_should_pass ... ok
test_base.test_should_raise_error ... ok
test_base.test_check_if_true_is_true ... ok
test_base.test_check_if_inc_works ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
from flask import Flask
app = Flask(__name__)
@app.route('/')
def example():
return '{"name":"Bob"}'
if __name__ == '__main__':
app.run()
Start Flask
flask run
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [01/Aug/2019 12:19:00] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [01/Aug/2019 12:19:00] "GET /favicon.ico HTTP/1.1" 404 -
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept All”, you consent to the use of ALL the cookies. However, you may visit "Cookie Settings" to provide a controlled consent.
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
Cookie
Duration
Description
cookielawinfo-checkbox-analytics
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checkbox-functional
11 months
The cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checkbox-necessary
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-others
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-performance
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
viewed_cookie_policy
11 months
The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.