Five common mistakes of newlyweds

Time:2021-5-5

Seeing this, I guess you must have seen some videos of blogs and technology conferences. Now you should be ready to set foot on itangular2This is no way back! So what do we need to know when we are on the road?

Here is a summary of some common novice errors, when you want to start your ownangular2When traveling, try to avoid it.

Note: in this article, I assume that you haveangular2I have some basic knowledge of English. If you’re absolutely new, you’ve only heard about it before and have no idea what it isangular2First, read the following materials:

Error 1: OriginalhiddenAttribute binding data

stayAngularJS 1If you want to switch the display state of DOM elements, you will useAngularJS 1Built in instructions such as:ng-showperhapsng-hide:

AngularJS 1Example:

<div ng-show="showGreeting">
   Hello, there!
</div>

andangular2The new template syntax allows you to bind an expression to any native attribute of a DOM element. This absolutely awesome function brings infinite possibilities. One of them is to bind the expression to the nativehiddenProperty, andng-showIt’s a bit like setting for elementsdisplay: none

angular2Of[hidden]Example (not recommended)

<div [hidden]="!showGreeting">
   Hello, there!
</div>

At first glance, the above example seems to beAngularJS 1Insideng-show. It’s not. They have a lot of problems!importantIt’s different.

ng-showandng-hideIt’s all through a callng-hideCSS class to controlDOMThe display state of the element,ng-hideClass simply sets the element todisplay: none. The key here is,AngularJS 1stayng-hideAdded in class!importantTo adjust the priority of the class so that it can override the elements from other stylesdisplayProperty.

Let’s go back to this example, primordialhiddenOn attributedisplay: noneStyles are implemented by browsers. Most browsers don’t use it!importantTo adjust its priority. Therefore, through the[hidden]="expression"To control the display state of elements, it is easy to be accidentally overridden by other styles. For example: if I write such a style for this element elsewheredisplay: flexIt’s better than the originalhiddenProperty has high priority(Look here)。

For this reason, we usually use*ngIfSwitch the existing state of elements to achieve the same goal:

angular2Of*ngIfExample (recommended)

<div *ngIf="showGreeting">
   Hello, there!
</div>

And the originalhiddenDifferent attributes,angular2In*ngIfNot constrained by style. No matter what CSS you write, it’s safe. But it’s necessary to mention that,*ngIfIt is not to control the display status of elements, but to directly add / delete elements from the template to achieve the effect of display or not.

Of course, you can also use the global style to create elementshiddenProperty to increase the hidden priority, for example:display: none !importantTo achieve this effect. You may ask, sinceangularThe group knows these questions, so why not give them directly in the frameworkhiddenHow about adding a global highest priority hidden style? The answer is that we can’t guarantee that global style is the best choice for all applications. Because this approach actually destroys those dependent on the originalhiddenAbility, so we leave the option to the engineer.

Error 2: direct callDOM APIs

Very few cases require direct operationDOMangular2Provides a series of high-order APIs of Niu x to achieve your desired goalDOMOperation, for example: queries. utilizeangular2The advantages of these APIs are as follows:

  • No direct operation in unit testDOMIt can reduce the test complexity and make your test cases run faster

  • Decoupling your code from the browser allows you to run your program in any rendering environment, such as:web worker, or leave the browser completely (for example, running on the server, orElectron(LI)

When you do it manuallyDOMAnd the more the code is written, the more difficult it is to read.

fromAngularJS 1(or not at allAngularFriends of transformation, I can guess which scenes you want to operate directlyDOMYes. Let’s take a look at these situations, and I’ll show you how to reconstruct them with queries.

Scenario 1: when you need to obtain an element in the current component template

Suppose you have one in your component templateinputTag, and you want to make it available as soon as the component loadsinputAuto focus

You may already know that@ViewChild/@ViewChildrenThese two queries can get the embedded components in the current component template. But in this case, what you need is to get a normal oneHTMLElement, not a component. You’re supposed to inject it directly at the beginningElementRefHere we go

Direct operationElementRef(not recommended)

@Component({
  selector: 'my-comp',
  template: `
    <input type="text" />
    <div> Some other content </div>
  `
})
export class MyComp {
  constructor(el: ElementRef) {
    el.nativeElement.querySelector('input').focus();
  }
}

In fact, what I want to say is that this kind of practiceunnecessary

Solution:@ViewChildWith local template variable

What programmers didn’t expect is that in addition to the component itself, other native elements can also be passed throughlocal variableI got it. When we write a component, we can directly write it in the component templateinputLabeling (e.g#myInput)And then pass the tag to the@ViewChildUsed to get the element. After the component is initialized, you can use therendererIn thisinputExecution on labelfocusThere’s no way.

@ViewChildcoordinationlocal variable(recommended)

@Component({
  selector: 'my-comp',
  template: `
    <input #myInput type="text" />
    <div> Some other content </div>
  `
})
export class MyComp implements AfterViewInit {
  @ViewChild('myInput') input: ElementRef;

  constructor(private renderer: Renderer) {}

  ngAfterViewInit() {
    this.renderer.invokeElementMethod(this.input.nativeElement,    
    'focus');
  }
}

Scenario 2: when you need to get a user mapped to an element in a component

What if the element you want to get is not in your component template definition? For example, suppose you have a list component that allows users to customize the list items, and then you want to track the number of list items.

Of course you can use it@ContentChildrenTo get the “content” in the component (which is user-defined and then mapped to the content in your component), but because these contents can be arbitrary values, they can’t be passed as beforelocal variableTo track them.

One way is to ask the user to add a predefinedlocal variable. In this case, the code can be changed from the above example to this:

@ContentChildrenandlocal variable(not recommended)

// user code
<my-list>
   <li *ngFor="#item of items" #list-item> {{item}} </li>
</my-list>

// component code
@Component({
  selector: 'my-list',
  template: `
    <ul>
      <ng-content></ng-content>
    </ul>
  `
})
export class MyList implements AfterContentInit {
  @ContentChildren('list-item') items: QueryList<ElementRef>;

  ngAfterContentInit() {
     // do something with list items
  }
}

However, this requires the user to write something extra(#list-item)It’s not really elegant! You may want users to just write<li>Label, don’t what#list-itemAttribute, what should we do?

Solution:@ContentChildrencoordinationliSelector instruction

Introduce a good plan and use it@DirectiveAdorner, match hisselectorFunction. Define a search / select function<li>Element, and then use the@ContentChildrenFilter the content that users map into the current component, leaving only the qualified onesliElements.

@ContentChildrencoordination@Directive(recommended)

// user code
<my-list>
   <li *ngFor="#item of items"> {{item}} </li>
</my-list>

@Directive({ selector: 'li' })
export class ListItem {}

// component code
@Component({
  selector: 'my-list'
})
export class MyList implements AfterContentInit {
  @ContentChildren(ListItem) items: QueryList<ListItem>;

  ngAfterContentInit() {
     // do something with list items
  }
}

Note: it seems that you can only choose<my-list>InsideliElements (for example:my-list li)It should be noted that at presentangular2“Parent child” mode selectors are not yet supported. If you need to get the elements in the component, use@ViewChildren@ContentChildrenThis kind of queries is the best choice

Error 3: using the acquired element in the constructor

When using queries for the first time, it’s easy to make such a mistake:

Print the result of query in the constructor (error)

@Component({...})
export class MyComp {
  @ViewChild(SomeDir) someDir: SomeDir;

  constructor() {
    console.log(this.someDir);// undefined
  }
}

When you see it printed outundefinedAfter that, you may think that your query can’t be used at all, or is there something wrong with the constructor. In fact, you just used the data too early. It must be noted that the result set of query cannot be used in component construction.

Fortunately,angular2Provides a new life cycle management hook, which can easily help you figure out when all kinds of queries are available.

  • If you are using view query (for example:@ViewChild@ViewChildren)The result set is available after the view is initialized. You can use itngAfterViewInithook

  • If you are using content query (for example:@ContentChild@ContentChildren)The result set is available after the content is initialized. You can use itngAfterContentInithook

Let’s change the above example

stayngAfterViewInitPrint query result set in windows (recommended)

@Component({...})
export class MyComp implements AfterViewInit {
  @ViewChild(SomeDir) someDir: SomeDir;

  ngAfterViewInit() {
    console.log(this.someDir);// SomeDir {...}
  }
}

Error 4: usengOnChangesDetect the change of query result set

stayAngularJS 1If you want to monitor a data change, you need to set a$scope.$watchAnd then manually judge whether the data has changed in each digest cycle. stayangular2Inside,ngOnChangesHooks make the process extremely simple. As long as you define it in the componentngOnChangesMethod, which is called automatically when the input data changes. This is awesome!

But it should be noted that,ngOnChangesCalled if and only if the component input data changes, “input data” refers to the@InputThe data explicitly specified by the decorator. If it is@ViewChildren@ContentChildrenAdded / deleted data in the result set of,ngOnChangesIt will not be called.

If you want to be notified when the query result set changes, you can’t use itngOnChanges. You should query the result set through thechangesProperty to subscribe to its built-in observable. As long as you subscribe successfully in the correct hook (not in the constructor), you will be notified when the result set changes.

For example, the code should look like this:

adoptchangesSubscribe to observable to monitor the change of query result set (recommended)

@Component({ selector: 'my-list' })
export class MyList implements AfterContentInit {
  @ContentChildren(ListItem) items: QueryList<ListItem>;

  ngAfterContentInit() {
    this.items.changes.subscribe(() => {
       // will be called every time an item is added/removed
    });
  }
}

If you don’t know anything about observables, hurry up,Look here

Error 5: misuse*ngFor

stayangular2In this paper, we introduce a new concept called “structural directives”, which is used to describe the expressions based on theDOMAn instruction to add or delete elements on or off the. Unlike other instructions, “structural directive” either acts on the template tag, or is used with template attribute, or is prefixed with “*” as a shorthand syntax. Because of this new grammatical feature, beginners often make mistakes.

Can you tell the following mistakes?

FALSEngForusage

// a:
<div *ngFor="#item in items">
   <p> {{ item }} </p>
</div>

// b:
<template *ngFor #item [ngForOf]="items">
   <p> {{ item }} </p>
</template>

// c:
<div *ngFor="#item of items; trackBy=myTrackBy; #i=index">
   <p>{{i}}: {{item}} </p>
</div>

Let’s solve the problem step by step

5A: replace “in” with “of”

// incorrect
<div *ngFor="#item in items">
   <p> {{ item }} </p>
</div>

If soAngularJS 1Experience, it’s usually easy to make this mistake. stayAngularJS 1The same repeater writingng-repeat="item in items"

angular2Replacing “in” with “of” is to match thefor-ofThe loop is consistent. It is also necessary to remember that if you do not use “*” grammar sugar, then the complete repeater writing method should be writtenngForOfInstead ofngForIn

// correct
<div *ngFor="#item of items">
   <p> {{ item }} </p>
</div>

5B: mix grammar sugar with complete grammar

// incorrect
<template *ngFor #item [ngForOf]="items">
   <p> {{ item }} </p>
</template>

There’s no need to mix it up – and in fact, it doesn’t work. When you use the grammar sugar (prefix “*”), the,angular2It will treat her as a template attribute instead of a general instruction. Specifically, the parser got itngForAfter the string, addngForAnd then parse it as a template attribute. The code is as follows:

<div *ngFor="#item of items">

It’s like this:

<div template="ngFor #item of items">

When you mix it up, it turns out like this:

<template template="ngFor" #item [ngForOf]="items">

From the perspective of template attribute, there is only one template attributengForNothing else. It is inevitable that the analysis will not be correct and will not work properly.

From the perspective of template tag, he is missing another onengForCommand, so it will also report an error. be withoutngForInstructions,ngForOfI don’t know who to blame.

It can be corrected in this way, either remove the “*” to write the complete format, or write it in the abbreviated form of “*” grammar

// correct
<template ngFor #item [ngForOf]="items">
   <p> {{ item }} </p>
</template>

// correct
<p *ngFor="#item of items">
   {{ item }}
</p>

5C: wrong operator in short form

// incorrect
<div *ngFor="#item of items; trackBy=myTrackBy; #i=index">
   <p>{{i}}: {{item}} </p>
</div>

In order to explain what’s wrong here, let’s write the code correctly without shorthand to see what it looks like

// correct
<template ngFor #item [ngForOf]="items" [ngForTrackBy]="myTrackBy" #i="index">
   <p> {{i}}: {{item}} </p>
</template>

In the complete form, the structure is easy to understand. Let’s try to decompose it

  • We input properties to thengForTwo sets of data are passed in

    • Bind tongForOfOriginal data collection onitems

    • Bind tongForTrackByCustom track by function on

  • use#Two statements were madelocal template variablesThey are:#iand#itemngForThe instruction is traversingitemsTo assign values to these two variables

    • iIt starts at 0itemsThe subscript of each element

    • itemIs the element corresponding to each subscript

When we use “*” syntax to abbreviate code, we must follow the following principles so that the parser can understand the abbreviated syntax:

  • All configurations should be written in the*ngForIn the attribute value of

  • adopt=Operator settingslocal variable

  • adopt:Operator to set input properties

  • Remove thengForPrefix, for example:ngForOfIt’s just writtenofThat’s it

  • Use semicolons to separate

According to the above specifications, the code is modified as follows:

// correct
<p *ngFor="#item; of:items; trackBy:myTrackBy; #i=index">
   {{i}}: {{item}}
</p>

Semicolons and colons are optional, and the parser ignores them. It’s just written to improve the readability of the code. Therefore, a little more can be omitted:

// correct
<p *ngFor="#item of items; trackBy:myTrackBy; #i=index">
   {{i}}: {{item}}
</p>

conclusion

I hope the explanation in this chapter is useful to you. Happy coding!

Original address:5 Rookie Mistakes to Avoid with Angular 2

Recommended Today

Analysis of super comprehensive MySQL statement locking (Part 1)

A series of articles: Analysis of super comprehensive MySQL statement locking (Part 1) Analysis of super comprehensive MySQL statement locking (Part 2) Analysis of super comprehensive MySQL statement locking (Part 2) Preparation in advance Build a system to store heroes of the Three KingdomsheroTable: CREATE TABLE hero ( number INT, name VARCHAR(100), country varchar(100), PRIMARY […]