Monday, May 11, 2020

ul padding 0

ul padding of 0 removes the bullets too

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'

@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);
        }
    }
}

Wednesday, March 11, 2020

Extracting components from Angular Routes

When I forgot to declare (in declarations) the components used in Angular Routes, it resulted to error: NG8002: Can't bind to 'ngModel' since it isn't a known property of 'input'


I decided to make a helper function:

function* getRoutesComponents(routes: Routes) {
    for (const route of routes) {
        if (route.component) {
            yield route.component;

            if (route.children) {
                yield* getRoutesComponents(route.children);
            }
        }
    }
}


Problem is, it gives these errors:




To enable runtime goodness, we need to disable AOT compilation, add --aot=false on angular commandline. However, disabling AOT would cause two major inconveniences.

First inconvenience, we cannot use constants as keys on objects (e.g., Angular Route) anymore. Doing so would result to example error: ERROR in Cannot read property 'loadChildren' of undefined




We can rectify the problem by avoiding the use of constants as keys:




Second inconvenience when AOT is disabled, despite fullTemplateTypeCheck is set to true, Angular won't be able to check name of the object(and properties) you are binding to ngModel if the name has a typo or misspellings. Angular will still happily build your project even if your template has full of typos or misspellings, which will result to runtime errors.



On the screenshot above, productNameBlahMeh does not exist on Product interface, yet Angular still build the project. This is due to AOT being disabled.


Those two inconveniences greatly outweighs the safety net for committing typos (or spelling mistakes). const can prevent typo errors as it enforces the single source of names in the code. It's hard to rely on developers not making typo or misspelling mistakes. fullTemplateTypeCheck only works if AOT is enabled.

Tuesday, March 10, 2020

NG8002: Can't bind to 'ngModel' since it isn't a known property of 'input'

This error will happen too even if the FormsModule is imported directly or indirectly (from shared module for example) in the feature module, if the imported component is not declared on declarations:



I followed Deborah Kurata's Angular Routing course, while I added the imported component ProductEditInfoComponent on Angular Route's component property, I forgot to add ProductEditInfoComponent on declarations property.

Adding the ProductEditInfoComponent on declarations property would solve the NG8002: Can't bind to 'ngModel' since it isn't a known property of 'input'. problem