Wednesday, May 27, 2020
.apply.bind, .call.bind
To make a shortcut for Math.max.apply(null, [5,8,6])
Use this:
This question was asked on stackoverflow:
Without the null on bind:
When maxArray([5,8,6]) is called, it is invoked like:
It's invoking the .apply on Math.max as:
The parameter [5,8,6] is assigned to this reference.
So both maxArray([5,8,6]) and Math.max.apply([5,8,6]) are basically invoking the Math.max without any parameters, and the [5,8,6] assigned to this reference.
We will see how .apply.bind works on our own object later and see its effect on this reference.
Both results to -Infinity:
We can invoke the maxArray properly like the following, but it somehow defeats the purpose of automagically passing the null on first parameter of Math.max.apply(null, [5,8,6]) expression
This is the result if we pass three parameters to Function.apply.bind:
As for the .call, it is the same as .apply, as both them always require an object to be passed on their first parameter, which will then gets assigned to this reference, usually null is used as reference for this for utility functions. .apply is more flexible than .call, as .apply can grow its parameter, whereas .call's parameters are fixed at invocation.
Guess the output of Math.max.call(8,7,6)?
Here are other observations:

Why .apply.bind requires two parameters:

As we can see, if we don't pass an object reference to first parameter of .bind and we pass only one parameter, that passed parameter gets assigned to *this* reference, as seen on theThis property on line 7, likewise on this[0] of line 8. Then the actual parameter of the method something receives nothing (undefined).
Use this:
var maxArray = Function.apply.bind(Math.max, null); var maxValue = maxArray([5,8,6]);
This question was asked on stackoverflow:
Why it's important to pass null for bind as argument?
Without the null on bind:
var maxArray = Function.apply.bind(Math.max);
When maxArray([5,8,6]) is called, it is invoked like:
It's invoking the .apply on Math.max as:
Math.max.apply([5,8,6]);
The parameter [5,8,6] is assigned to this reference.
So both maxArray([5,8,6]) and Math.max.apply([5,8,6]) are basically invoking the Math.max without any parameters, and the [5,8,6] assigned to this reference.
Math.max();
We will see how .apply.bind works on our own object later and see its effect on this reference.
Both results to -Infinity:
dev@Developers-iMac % node Welcome to Node.js v12.14.0. Type ".help" for more information. > var maxArray = Function.apply.bind(Math.max); undefined > maxArray([5,8,6]); -Infinity > Math.max.apply(); -Infinity > Math.max(); -Infinity >
We can invoke the maxArray properly like the following, but it somehow defeats the purpose of automagically passing the null on first parameter of Math.max.apply(null, [5,8,6]) expression
dev@Developers-iMac % node Welcome to Node.js v12.14.0. Type ".help" for more information. > var maxArray = Function.apply.bind(Math.max); undefined > maxArray(null, [5,8,6]); 8
This is the result if we pass three parameters to Function.apply.bind:
dev@Developers-iMac % node Welcome to Node.js v12.14.0. Type ".help" for more information. > var maxArray = Function.apply.bind(Math.max, null, [10, 40, 30]); undefined > maxArray() 40 > maxArray([50,60]) 40 >
As for the .call, it is the same as .apply, as both them always require an object to be passed on their first parameter, which will then gets assigned to this reference, usually null is used as reference for this for utility functions. .apply is more flexible than .call, as .apply can grow its parameter, whereas .call's parameters are fixed at invocation.
Guess the output of Math.max.call(8,7,6)?
dev@Developers-iMac % node Welcome to Node.js v12.14.0. Type ".help" for more information. > Math.max(8,7,6) 8 > Math.max.call(8,7,6) 7 > Math.max.apply(8,7,6) Thrown: TypeError: CreateListFromArrayLike called on non-object > Math.max.apply([8,7,6]) -Infinity > Math.max.apply([8,7,6],[2,4,3]) 4 > Math.max.apply(null,[2,4,3]) 4 > Math.max.call(null,8,7,6) 8
Here are other observations:

Why .apply.bind requires two parameters:

As we can see, if we don't pass an object reference to first parameter of .bind and we pass only one parameter, that passed parameter gets assigned to *this* reference, as seen on theThis property on line 7, likewise on this[0] of line 8. Then the actual parameter of the method something receives nothing (undefined).
Monday, May 11, 2020
Tuesday, May 5, 2020
flex is flexible, but it's not griddable
<div id="band">
<div style="background-color: red">John</div>
<div style="background-color: green">Paul</div>
<div style="background-color: blue">George</div>
<div style="background-color: yellow">Ringo</div>
</div>
<style>
body {
background-color: slategray;
display: flex;
justify-content: center;
align-items: center;
}
#band {
background-color: white;
margin: 0 auto;
border-style: solid;
border-width: 2px;
max-width: 740px;
width: 100%;
display: flex;
/* justify-content: flex-start; */ /* flex-start is the default justify-content */
flex-wrap: wrap;
}
#band > div {
width: 200px;
height: 200px;
/* center all the names inside each box */
display: flex;
justify-content: center;
align-items: center;
}
</style>
Its output is:

If we change the justify-content to center:
display: flex; justify-content: center; flex-wrap: wrap;
Not only the first row's items are centered, the last item will be centered as well, which is not we want:

If we want to wrap extra items at leftmost and keep the whole grid of items at the center, we need to use grid instead of flex, and replace flex-wrap: wrap to repeat autofit and minmax. To wit:
display: grid; justify-content: center; grid-template-columns: repeat(auto-fit, minmax(200px, max-content));
The output is correct:

On the above display grid and grid-template-columns settings, if we change the justify-content to end:
display: grid; justify-content: end; /* flex-end works as well, but we will just be confusing ourselves with that, we are using grid not flex */ grid-template-columns: repeat(auto-fit, minmax(200px, max-content));
The output is:

To contrast with flex-end of display: flex:
display: flex; justify-content: flex-end; flex-wrap: wrap;
The output is:

Same settings above, but with five items:

Let's use grid again with justify-content of end:
display: grid; justify-content: end; grid-template-columns: repeat(auto-fit, minmax(200px, max-content));
Its output:

This flex's flex-start:
display: flex; justify-content: flex-start; flex-wrap: wrap;
And grid's start:
display: grid; justify-content: start; grid-template-columns: repeat(auto-fit, minmax(200px, max-content));
Have same output:
Thursday, April 30, 2020
Don't use label display block for radio and checkbox
<div class="form-group">
<label id="recommend-label" class="label">Would you recommend freeCodeCamp to a friend?</label>
<label><input type="radio" name="recommend" value="definitely">Definitely the greatest tutorial on earth</label>
<label><input type="radio" name="recommend" value="maybe">Maybe</label>
<label><input type="radio" name="recommend" value="not-sure">Not sure</label>
</div>
<style>
.form-group label:not(.label) {
display: block;
background: lightgreen;
margin: 2px;
}
</style>
The problem with label display block approach is that it makes the trailing spaces after the label clickable too.
Demo: https://jsfiddle.net/qadxjghy/
To make the label's trailing spaces not clickable, enclosed the label in div. And then change the input's enclosing label display to inline-flex
<div class="form-group">
<label id="recommend-label" class="label">Would you recommend freeCodeCamp to a friend?</label>
<div>
<label><input type="radio" name="recommend" value="definitely">Definitely the greatest tutorial on earth</label>
</div>
<div>
<label><input type="radio" name="recommend" value="maybe">Maybe</label>
</div>
<div>
<label><input type="radio" name="recommend" value="not-sure">Not sure</label>
</div>
</div>
.form-group label:not(.label) {
display: inline-flex;
background: lightgreen;
margin: 2px;
}
Output:
Demo: https://jsfiddle.net/qadxjghy/1/
display: inline-block can be used as well:
inline-flex is better, so we can use align-items: baseline to align the baseline of text to the baseline of the radio/checkbox buttons:
display attribute can be removed as well, but you can't set things like margin:
Friday, March 20, 2020
Angular Service
Simpler way to make Angular service available to component, just add providedIn: 'root'
Then on consuming component:
To make it more explicit:
Then on consuming component:
@Injectable({providedIn: 'root'})
export class ProductService {
Then on consuming component:
@Component({
selector: 'pm-products',
templateUrl: './product-list.component.html'
// no need to add provider
})
export class ProductListComponent {
constructor(private _productService: ProductService)
}
To make it more explicit:
@Injectable()
export class ProductService {
Then on consuming component:
@Component({
selector: 'pm-products',
templateUrl: './product-list.component.html'
providers: [ProductService] // explicitly add the service here
})
export class ProductListComponent {
constructor(private _productService: ProductService)
}
Thursday, March 12, 2020
Use .filter? Sorry, no control flow analysis joy for you
function* getRoutesComponentsX(routes: Routes) {
yield* routes
.filter(route => route.component)
.map(route => route.component);
yield* routes
.filter(route => route.children)
.map(route => Array.from(getRoutesComponentsX(route.children!)))
.reduce((a, b) => a.concat(b), []);
}
TypeScript won't be able to follow the flow of control when using .filter instead of if statement.
That would sometimes lead to use of non-null assertion operator.
Removing the non-null assertion operator would lead to this error:

Here's essentially same code, albeit using just if statements, no more need to use the non-null assertion operator.
function* getRoutesComponents(routes: Routes) {
for (const route of routes) {
if (route.component) {
yield route.component;
}
if (route.children) {
yield* getRoutesComponents(route.children);
}
}
}
Subscribe to:
Posts (Atom)





