I am a relative beginner in Angular, and I am struggling to understand some source I am reading from the ng-bootstrap project. The source code can be found here.
I am very confused by the code in ngOnInit:
ngOnInit(): void {
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
const results$ = letProto.call(inputValues$, this.ngbTypeahead);
const processedResults$ = _do.call(results$, () => {
if (!this.editable) {
this._onChange(undefined);
}
});
const userInput$ = switchMap.call(this._resubscribeTypeahead, () => processedResults$);
this._subscription = this._subscribeToUserInput(userInput$);
}
What is the point of calling .call(...) on these Observable functions? What kind of behaviour is this trying to achieve? Is this a normal pattern?
I've done a lot of reading/watching about Observables (no pun intended) as part of my Angular education but I have never come across anything like this. Any explanation would be appreciated
My personal opinion is that they were using this for RxJS prior 5.5 which introduced lettable operators. The same style is used internally by Angular. For example: https://github.com/angular/angular/blob/master/packages/router/src/router_preloader.ts#L91.
The reason for this is that by default they would have to patch the Observable class with rxjs/add/operators/XXX. The disadvantage of this is that some 3rd party library is modifying a global object that might unexpectedly cause problems somewhere else in your app. See https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md#why.
You can see at the beginning of the file that they import each operator separately https://github.com/ng-bootstrap/ng-bootstrap/blob/master/src/typeahead/typeahead.ts#L22-L25.
So by using .call() they can use any operator and still avoid patching the Observable class.
To understand it, first you can have a look at the predefined JavaScript function method "call":
var person = {
firstName:"John",
lastName: "Doe",
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var myObject = {
firstName:"Mary",
lastName: "Doe",
}
person.fullName.call(myObject); // Will return "Mary Doe"
The reason of calling "call" is to invoke a function in object "person" and pass the context to it "myObject".
Similarly, the reason of this calling "call" below:
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
is providing the context "this._valueChanges", but also provide the function to be called base on that context, that is the second parameter, the anonymous function
value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
}
In the example that you're using:
this._valueChanges is the Input Event Observerable
The _do.call is for doing some side affects whenever the event input happens, then it returns a mirrored Observable of the source Observable (the event observable)
UPDATED
Example code: https://plnkr.co/edit/dJNRNI?p=preview
About the do calling:
You can call it on an Observable like this:
const source = Rx.Observable.of(1,2,3,4,5);
const example = source
.do(val => console.log(`BEFORE MAP: ${val}`))
.map(val => val + 10)
.do(val => console.log(`AFTER MAP: ${val}`));
const subscribe = example.subscribe(val => console.log(val));
In this case you don't have to pass the first parameter as the context "Observable".
But when you call it from its own place like you said, you need to pass the first parameter as the "Observable" that you want to call on. That's the different.
as #Fan Cheung mentioned, if you don't want to call it from its own place, you can do it like:
const inputValues$=this._valueChanges.do(value=>{
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
})
I suppose
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
is equivalent to
const inputValues$=this._valueChanges.do(value=>{
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
})
In my opinion it's not an usual pattern(I think it is the same pattern but written in different fashion) for working with observable. _do() in the code is being used as standalone function take a callback as argument and required to be binded to the scope of the source Observable
https://github.com/ReactiveX/rxjs/blob/master/src/operator/do.ts
Related
There's a commonly used utility hook "useLatest", which returns a ref containing the latest value of the input. There are 2 common implementations:
const useLatest = <T>(value: T): { readonly current: T } => {
const ref = useRef(value);
ref.current = value;
return ref;
};
From https://github.com/streamich/react-use/blob/master/src/useLatest.ts
const useLatest = <T extends any>(current: T) => {
const storedValue = React.useRef(current)
React.useEffect(() => {
storedValue.current = current
})
return storedValue
}
From https://github.com/jaredLunde/react-hook/blob/master/packages/latest/src/index.tsx
The first version isn't suitable for React 18's concurrent mode, the second version will return the old value if used before useEffect runs (e.g. during render).
Is there a way to implement this that's both concurrent-safe and consistently returns the correct value?
Here's my attempt:
function useLatest<T>(val: T): React.MutableRefObject<T> {
const ref = useRef({
tempVal: val,
committedVal: val,
updateCount: 0,
});
ref.current.tempVal = val;
const startingUpdateCount = ref.current.updateCount;
useLayoutEffect(() => {
ref.current.committedVal = ref.current.tempVal;
ref.current.updateCount++;
});
return {
get current() {
// tempVal is from new render, committedVal is from old render.
return ref.current.updateCount === startingUpdateCount
? ref.current.tempVal
: ref.current.committedVal;
},
set current(newVal: T) {
ref.current.tempVal = newVal;
},
};
}
This hasn't been thoroughly tested, just wrote it while writing this question, but it seems to work most of the time. It should be better than both versions above, but it has 2 issues: it returns a different object every time and it's still possible to be inconsistent in this scenario:
Render 1:
ref1 = useLatest(val1)
Create function1, which references ref1
Commit (useLayoutEffect runs)
Render 2:
useLatest(val2)
Call function1
function1 will use val1, but it should use val2.
Here is what I think is correct:
const useLatest = <T extends any>(current: T) => {
const storedValue = React.useRef(current)
React.useLayoutEffect(() => {
storedValue.current = current
})
return storedValue.current
}
Is there a way to implement this that's both concurrent-safe and consistently returns the correct value?
The question doesn't actually explain what "this" means, i.e. how is useLatest called, and what purpose it fulfills in the application. So I'll have to guess for that ;) A somewhat realistic example would be very helpful.
In any case, it's probably useful to take a step back and ask if useLatest is the most suitable solution. If you find you don't need it, you also won't have to fix it.
With the way it works (depending on an effect to capture the value), it indeed won't play well with concurrent features. But even without them, it's an unreliable approach as the ref theoretically can change at any point, making renders unpredictable.
My guess of the use case is something similar to the proposed (and partially accepted) useEvent hook (GitHub PR).
function Chat() {
const [text, setText] = useState('');
const onClick = useEvent(() => {
sendMessage(text);
});
return <SendButton onClick={onClick} />;
}
Its purpose is to capture the latest render's scope, like useCallback, but without the need for dependencies. It does this by using an unchanging callback that internally calls the latest created callback, in a ref that is re-assigned on every render. That way passing that callback as a prop won't cause any renders by itself.
You can implement this yourself already, but the RFC mentions some open questions about this approach.
export function useEvent(handler) {
const latestHandlerRef = useRef();
useLayoutEffect(() => {
latestHandlerRef.current = handler;
});
// Never changing callback.
return useCallback((...args) => {
latestHandlerRef.current(...args)
}, []);
}
I also tested setting latestHandlerRef.current = handler directly in render instead of the layout effect. For now this seems to work as expected but that's just my use case. In the PR some doubt is expressed over assigning to a ref during render, though possibly these concerns don't really apply here, as the ref is only ever accessed in the callback.
From here: https://angular.io/tutorial/toh-pt4 - hero.component.ts
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes);
}
I have understood that this heroes => this.heroes = heroes translates to as follows:
f( heroes )
{
return this.heroes = heroes;
}
Is return implied here?
I want to understand where from does this inner function get called. Who's calling it?
In the example code you provided,to return a list of heros, we have to write it like
getHeroes(): Hero[] {
this.heroService.getHeroes()
.subscribe(heroes => ({heroes : heroes}));
}
Above arrow function will translate to
f( heroes )
{
return this.heroes = heroes;
}
Otherwise, the code you provided, its just making an assignment to this.heros variable, so return statement does not apply, and the arrow function translate to
f( heroes )
{
this.heroes = heroes;
}
Further explanation of Arrow function
Arrow functions, like function expressions, can be used to return an object literal expression. The only caveat is that the body needs to be wrapped in parentheses, in order to distinguish between a block and an object (both of which use curly brackets).
Example
//ES5
var setNameIdsEs5 = function setNameIds(id, name) {
return {
id: id,
name: name
};
};
// ES6
var setNameIdsEs6 = (id, name) => ({ id: id, name: name });
console.log(setNameIdsEs6 (4, "Kyle")); // Object {id: 4, name: "Kyle"}
For a regular function, if the ‘this’ keyword were inside an object’s method (a function that belongs to an object), it would refer to the object. While in an arrow function, ‘this’, always references the owner of the function it is in. Adding a console.log(this) before the return in the arrow function returns a Window object.
Example
// ES5
const brunch = {
food: 'Dim sum',
beverage: 'Jasmine tea',
order: function() {
return `I'll have the ${this.food} with ${this.beverage} please.`
}
}
brunch.order(); //"I'll have the Dim sum with Jasmine tea please."
// ES6
const brunch = {
food: 'Dim sum',
beverage: 'Jasmine tea',
order: () => {
return `I'll have the ${this.food} with ${this.beverage} please.`
}
}
brunch.order(); //"I'll have the undefined with undefined please."
In the following arrow function heroes => this.heroes = heroes
Yes, return is implied, this may or may not be a problem depending on the rest of your code. I'm assuming in your case, you're not looking to return the assignment, you just want the assignment to happen.
So all you have to do is wrap the result in braces like so heroes => {this.heroes = heroes}, now nothing is returned.
As for your second question, who calls the inner arrow function: From the looks of your code, this.heroService.getHeroes() returns an observable. In your code you have "subscribed" to the observable. The easy way to think about is that the observable is telling your subscription to run the arrow function.
Basically, you are observing the observable. The observable needs time to think about it's response which is why you need to subscribe to it. When it is ready it will tell you it's response, and your arrow function inside the subscription is your response to the observable response. This is a very non-technical description to give you some intuition.
You should research observables/async functions to get a clear understanding.
this.heroService.getHeroes() returns a subscription object. In order to subscribe to it we should use .subscribe(heroes => this.heroes = heroes);.
subscribe maps the subscription object and extracts the data from the object and puts it in this.heroes, the first param inside the subscribe.
Then with the => operator we start writing actions like affectation, displaying, or anything to do, you can do anything after =>
in order to write more than one action you should use the braces like this:
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => { console.log('ur data from the observable',heroes);
this.heroes = heroes
});
}
I don't know JS/ES6 well enough to describe my question in code. So most of this question is conceptually and in pseudo code.
Say I have a Contractor class like this:
class Contractor {
constructor(jobFn) {
// save jobFn;
}
dailyRoutine() {
// let result = DriveToWork()
const result = 6
DoTheJob(result)
DriveBackHome()
}
}
The problem is, what the DoTheJob() does might be different things in different places.
So in place A, it could be
he = new Contractor(write_front_end(with, this, and that))
And in place B, it could be
he = new Contractor(fix_backend_node(with, express))
I.e., the behavior need to be passed in during the constructor, and the action might need to take different kind and different amount of parameters.
Would such thing be possible with ES6?
Please show ES6 code that can pass function with different kind and different amount of parameters through the constructor to DoTheJob().
Further, the challenge is that the jobFn need to be a Curried function, meaning there is one or more parameter missing to do the DoTheJob job. Say if the jobFn is passed with Curried add(3), then DoTheJob will do UncurriedAdd of add(3, 6); if then jobFn is passed with Curried multiple(5), then DoTheJob will do Uncurried of multiple(5, 6);
Just assign the passed function to this.DoTheJob, and then call this.DoTheJob inside dailyRoutine:
class Contractor {
constructor(jobFn) {
this.DoTheJob = jobFn;
}
dailyRoutine() {
// DriveToWork()
this.DoTheJob();
// DriveBackHome()
}
}
const c1 = new Contractor(() => console.log('doing job A'));
c1.dailyRoutine();
const c2 = new Contractor(() => console.log('doing job B'));
c2.dailyRoutine();
// c1 again:
c1.dailyRoutine();
// feel free to reference any in-scope variables in the passed function,
// no need to pass the variables as additional parameters
const data = 'data';
const c3 = new Contractor(() => console.log('data is', data));
c3.dailyRoutine();
If dailyRoutine needs to be invoked with data that needs to be sent to the passed doTheJob function, just define the needed arguments in the function you pass, there's no need for actual currying here:
class Contractor {
constructor(jobFn) {
this.DoTheJob = jobFn;
}
dailyRoutine(doJobArg) {
this.DoTheJob(doJobArg);
}
}
// feel free to reference any in-scope variables in the passed function,
// no need to pass the variables as additional parameters
const data = 'data';
const c3 = new Contractor((arg) => console.log('data is', data, 'and arg is', arg));
c3.dailyRoutine('argDoTheJobIsCalledWith');
In my case, I may advise you that it's better to give the predicate to dailyRoutine, because this way you'll be able to reuse the same instance and give different predicates.
Anyway, there's a pure OOP solution for this, using method polymorphism, the JavaScript way (aka duck typing):
class Contractor {
driveBackHome() {}
dailyRoutine() {
const result = 6
this.doTheJob(result)
this.driveBackHome()
}
}
class SpecializedContractorA extends Contractor {
doTheJob(result) {
console.log('aaaaaaaaaaaaaaaaaaaaa', result)
}
}
class SpecializedContractorB extends Contractor {
doTheJob(result) {
console.log('bbbbbbbbbbbbbbbb', result)
}
}
const a = new SpecializedContractorA()
a.dailyRoutine()
const b = new SpecializedContractorB()
b.dailyRoutine()
I have this load-more listener on a button that calls the functions and it works fine.
let moviesPage = 1;
let seriesPage = 1;
document.getElementById('load-more').addEventListener('click', () => {
if (document.querySelector('#movies.active-link')) {
moviesPage++;
getMovies(moviesPage);
//getMovies(genreId, moviesPage);
} else if (document.querySelector('#series.active-link')) {
seriesPage++;
getSeries(seriesPage);
}
});
Now I have another listener on a list of links that calls the following code. It takes the genreId from the event parameter to sent as an argument to the api call. Also works fine so far.
document.querySelector('.dropdown-menu').addEventListener('click',
getByGenre);
function getByGenre (e) {
const genreId = e.target.dataset.genre;
movie.movieGenre(genreId)
.then(movieGenreRes => {
ui.printMovieByGenre(movieGenreRes);
})
.catch(err => console.log(err));
};
What I want to do is to call getByGenre from the load-more listener while passing also the moviesPage argument as you can see on the commented code so it can also be passed to the api call.
What would be the best way to do that? I've looked into .call() and .bind() but I'm not sure if it's the right direction to look at or even how to implement it in this situation.
Short Answer
Kludge: Global State
The simplest, though not the most elegant, way for you to solve this problem right now is by using some global state.
Take a global selection object that holds the selected genreId. Make sure you declare the object literal before using it anywhere.
So, your code might look something like so:
var selection = { };
document.querySelector('.dropdown-menu').addEventListener('click',
getByGenre);
function getByGenre (e) {
const genreId = e.target.dataset.genre;
selection.genreId = genreId;
movie.movieGenre(...);
};
...
let moviesPage = 1;
let seriesPage = 1;
document.getElementById('load-more').addEventListener('click', () => {
if (document.querySelector('#movies.active-link')) {
...
if (selection.genreId !== undefined) {
getMovies(selection.genreId, moviesPage);
}
} else if (...)) {
...
}
});
Closure
A more elegant way for you to accomplish this is by using a closure, but for that I have to know your code structure a bit more. For now, global state like the above will work for you.
Longer Answer
Your concerns have not been separated. You are mixing up more than one concern in your objects.
For e.g. to load more movies, in your load-more listener, you call a function named getMovies. However, from within the .dropdown-menu listener, you call into a movie object's method via the getByGenre method.
Ideally, you want to keep your UI concerns (such as selecting elements by using a query selector or reading data from elements) separate from your actual business objects. So, a more extensible model would have been like below:
var movies = {
get: function(howMany) {
if (howMany === undefined) {
howMany = defaultNumberOfMoviesToGetPerCall;
}
if (movies.genreId !== undefined) {
// get only those movies of the selected genre
} else {
// get all kinds of movies
}
},
genreId : undefined,
defaultNumberOfMoviesToGetPerCall: 25
};
document.get...('.load-more').addEventListener('whatever', (e) => {
var moviesArray = movies.get();
// do UI things with the moviesArray
});
document.get...('.dropdown-menu').addEventListener('whatever', (e) => {
movies.genreId = e.target.dataset.genreId;
var moviesArray = movies.get();
// do UI things with the moviesArray
});
I have a segment of code where I am getting some weird output. The parameter being used in the function is changing when I would not think it would.
entry point to the code.
handleAction(action : HAction){
this.openForm("marksForm","Form");
}
method to open the form.
public openForm(name : string, type : string){
console.log("Name",name)
let cb = this.createComponentInitCallback(this.compService.getComponentType(type),
name);
let itemconfig ={
type: 'row',
content: [{
type: 'component',
title: 'Form Test',
componentName: 'h-form',
componentState: {}
}]
}
let tryRegister = false;
try{
this.goldenLayout.getComponent(name);
}catch(e){console.log("registering component",name); tryRegister=true;}
if(tryRegister)
this.goldenLayout.registerComponent(name,cb);
if(this.goldenLayout.root.contentItems[0])
this.goldenLayout.root.contentItems[ 0 ].addChild(itemconfig);
else
this.goldenLayout.root.addChild(itemconfig);
}
This method creates the defined callback function.
public createComponentInitCallback(componentType: Type<any>, name : string ): ComponentInitCallback {
console.log("1Name",name);
let f = (container: GoldenLayout.Container, componentState: any) => {
console.log("2Name",name);
this.ngZone.run(() => {
console.log("3Name",name);
// Create an instance of the angular component.
const factory = this.componentFactoryResolver.resolveComponentFactory(componentType);
const injector = this._createComponentInjector(container, componentState);
const componentRef = this.viewContainer.createComponent(factory, undefined, injector);
console.log("4Name",name)
componentRef.instance.name=name;
// Bind the new component to container's client DOM element.
container.getElement().append($(componentRef.location.nativeElement));
this._bindEventHooks(container, componentRef.instance);
// Store a ref to the compoenentRef in the container to support destruction later on.
(container as any)[COMPONENT_REF_KEY] = componentRef;
});
};
return f;
}
You will see my log statements. This callback gets executed inside the GoldenLayout library. However, I was pretty sure this should work.
Below are the outputs:
Name marksForm
1Name marksForm
2Name h-form
3Name h-form
4Name h-form
The first console output is logging what is passed into this method. You can see that it is obviously changing on me so I have to be doing something wrong. Oddly enough, the componentType parameter is working perfectly fine.
What am I doing wrong here?
A function that relies on lexical this and is supposed to be passed as callback should always be bound to the context.
createComponentInitCallback method can be bound to the context, either with bind or an arrow function (see this explanation on bound prototype methods vs arrow instance methods):
constructor() {
this.createComponentInitCallback = this.createComponentInitCallback.bind(this);
}
Or resulting callback can be bound to the context:
let cb = this.createComponentInitCallback(this.compService.getComponentType(type),
name).bind(this);
Considering there are no scenarios where this should differ from current class instance, the first option is preferable.
As for function scope, it cannot be lost under no circumstances. If name was passed as an argument in parent function, it will remain unchanged in nested function.
Unless you are using Angular HttpModule to make calls, any async call made with an external library will result in running your call back out of the original scope.
To mitigate this you need to assign this to a local variable the callback can use.
public createComponentInitCallback(componentType: Type<any>, name : string ): ComponentInitCallback {
console.log("1Name",name);
let self = this;
let f = (container: GoldenLayout.Container, componentState: any) => {
console.log("2Name",name);
this.ngZone.run(() => {
console.log("3Name",name);
// Create an instance of the angular component.
const factory = self.componentFactoryResolver.resolveComponentFactory(componentType);
const injector = self._createComponentInjector(container, componentState);
const componentRef = self.viewContainer.createComponent(factory, undefined, injector);
console.log("4Name",name)
componentRef.instance.name=name;
// Bind the new component to container's client DOM element.
container.getElement().append($(componentRef.location.nativeElement));
self._bindEventHooks(container, componentRef.instance);
// Store a ref to the compoenentRef in the container to support destruction later on.
(container as any)[COMPONENT_REF_KEY] = componentRef;
});
};
return f;
}