Angularjs practice of YY game cloud (Reprinted)

Time:2021-5-12

Why angularjs
Easy to build single page application
It can be said that this is an important reason why we finally choose angularjs. If you want to build a single page application with clear structure, maintainability, high development efficiency and good experience, angularjs is quite a good framework.

Charm of single page application

What is a single page application? Single page application refers to a web-based application or website. The page is always a partial update element, not a whole page refresh. It gives users the feeling that the whole website is a page. When users click a menu or key, they will not jump to other pages. The front end will get the data of the corresponding page from the back end instead of HTML, and then update the content of the page locally and dynamically. If it is a traditional multi page website, when users visit different pages, the server will directly return an HTML, Then the browser is responsible for presenting the HTML to the user. At present, most of the cloud console is a single page application architecture, which can bring a more similar client experience than a web experience. The single page application website has the following advantages in terms of experience:

When doing “page Jump”, it is always a local dynamic refresh. Users will not feel the whole screen flicker, but only need to change the area to do a local refresh. For example, for two different pages, suppose that the page elements are the same, but the text in the elements is different (for example, each page has a breadcrumb, a row of buttons and a table, and the layout of these elements is the same). When the user jumps to another page, he will see that the whole page is not re rendered, but the text has changed. To put it simply, it’s a bit like using an app. It always changes locally. Which app have you ever seen? When you click on a different function view, the whole screen will flash white?

The URL can be collected and returned. If the URL of the browser remains unchanged, it can’t be called a real single page application. Different functions, different resource pages, the corresponding URL is not the same. When users jump to another function, they will find that the URL will change to the corresponding value; Through the back key, you can also return to the previous page. What’s the use of this feature? If the URL does not change with the function, when the user refreshes the current page, it will return to the previous default page instead of the expected current function page. Similarly, the URL does not have collectability, because clicking the previously collected URL is always the default page of the website. This is not a good experience for some management and control systems with strong interaction.

Fast and smooth function switching. Fast and smooth mainly because of two reasons: first, the page is a local refresh, from the user’s senses faster; Second, the content of communication with the background is data, not page template, so the amount of requests is less; While the traditional website, when visiting different pages, the server returns HTML, which is larger and needs to repeatedly load JavaScript and CSS files.

Because of the single page of the website, you can better use the interaction of global classes (when you switch pages, you need to keep the same interaction all the time). For example, you need to display the progress of a time-consuming operation on the page, such as the progress of uploading files, the current status of time-consuming operation, and so on. You can display the progress on the far right of the page. When a user accesses a single page application, he will understand that when he clicks other modules, the notification bar on the right will not disappear and will be displayed in a fixed way. If it is a multi page website, users will be confused and worried that the progress notification will disappear after they jump to other pages.

Angularjs is a powerful tool for developing single page applications

Single page applications have higher requirements for code layering and clear structure. Angularjs is an MVVM framework, and its own conventions reduce the possibility of writing “one pot of porridge” code (which will be detailed in the discussion of “writing more maintainable code” below).

UI router, the famous third-party component of angularjs, is a component that controls page routing. It supports us to quickly build single page applications (the routing function of angularjs itself is OK, but the function will be slightly weaker).

Angularjs’s check and update mechanism makes page refresh faster and more natural. When the data “may” change, angularjs will check all the changed elements, and then refresh all the changed UI elements “once”.

Write more maintainable code
Many people often complain that when people of different levels come together to write JavaScript, the final project is often a pot of porridge. In the same JavaScript file, all kinds of logic are mixed together. It’s a nightmare to add or delete functions. Nothing can be accomplished without norms or standards. As a framework, angularjs can greatly improve this situation, making the overall hierarchy of the project clear and responsibilities clear. In my opinion, angularjs can help us write more maintainable code, first, because of its “separation of concerns” concept, and second, because its powerful features save a lot of code for the project, thus reducing the probability of programmers making mistakes.

Separation of concerns

Separation of concerns is a major design philosophy of angularjs. The so-called separation of concerns refers to the clear responsibilities of each logical layer. For example, when you need to modify or even replace the presentation layer, you don’t need to pay attention to how the business layer is implemented. In angularjs, service layer (Ajax request) – business layer (controller) – presentation layer (HTML template) – interaction layer (animation) all have corresponding basic components. Different components have different responsibilities, so it is difficult to put the responsibilities of component B into component A. For example:

HTML and controller need to work together, but their responsibilities are clear. The logic of view and interaction level, such as displaying and hiding some elements, is the responsibility of HTML template, and controller can only be used for data initialization. If you do the opposite, it will be very awkward for you to do what the HTML template does in the controller. This is very important. Traditional JavaScript code often has a lot of DOM operation logic in it, as well as a lot of data operation related logic. When these logics are coupled together, when you need to reconstruct the data layer or view layer separately (such as project UI Revision), it will be very difficult. At the same time, Due to the rapid expansion of JavaScript code, maintenance is also very troublesome.

You can’t put the background communication logic into the controller, but into the factory. Background communication logic should be made public. Since controllers cannot call each other, it is impossible for you to put the background communication logic into one of the controllers, and then other controllers call the exposed interface of the controller. The only way is to put the background communication logic into factory or service.

It seems that filter and directive can be used for data conversion, but in fact they are different. Since filter can only format data and does not support the introduction of templates, public UI interaction can only be realized through direct when DOM elements are involved or HTML templates need to be introduced.

To sum up, the logic of the presentation layer and interaction layer of angularjs project is in HTML or instructions. The service layer (background communication) is only suitable for factory (or service), while the business layer is in the charge of the controller. In this way, the logic of each layer is frivolous, not intertwined. If you just want to optimize the display logic, you can change the HTML, regardless of how the controller is written. We have personal experience of this. In the process of project development, we reconstructed the visual effect, and all the HTML had to be rewritten. However, when refactoring, our controller, background communication (service) and filter basically do not need to be changed, just change HTML. If the project is written with jQuery, it is obviously impossible to do so. You need to add some classes or IDs for the new HTML to be used by jQuery selectors. Then you need to bind events in JavaScript to write new interaction effects according to the new CSS style name. On angularjs, you don’t need to do anything (for example, add new classes and IDs for HTML elements for jQuery selectors) id; Binding events in JavaScript), some (such as interaction effects) just need to change HTML instead of JavaScript.

Angularjs saves us a lot of code

For any project, bloated code and redundancy are the natural enemies of maintainability. Therefore, to achieve the same function, the less the code, the higher the abstraction and the lower the redundancy, which means that the project is more convenient to maintain to a certain extent. The ability to reduce the amount of code is also a big advantage of angularjs. Let’s see how it reduces the amount of code:

First of all, as a large and comprehensive framework (double-edged sword, both advantages and disadvantages), angularjs provides many features, so that we can focus more on the writing of business code.

Secondly, the bidirectional data binding feature of angularjs liberates us from a large number of value binding code. Compared with jQuery, angularjs doesn’t need to add some style independent class and 2-2-2. ID to HTML in order to select an element; You don’t need to write a bunch of code to take values and set values from HTML elements; There is no need to bind events in JavaScript code; There is no need to write code to update the HTML value when the JavaScript value changes. Two way data binding, let’s say goodbye to a lot of simple and boring binding events, binding value code.

Direct, filter, factory and so on are naturally reusable components, reducing redundant and repetitive code. Some logic that needs to be shared will be quite awkward if it is put in the controller. In this way, it is “forced” by angularjs to put all the common logic into directive, filter and factory.

Summary of development experience
My understanding of angularjs basic components
Simplify the use of several basic components

First of all, we need to clarify the usage scenarios of several components of angularjs. One of the problems with angularjs is that there are too many new concepts and features. Novices need to know so much at once, and the learning curve is a little steep. In order to help you understand, let’s summarize the usage scenarios of several major components that I understand.

Put the requested resources and data cache into service. Factory and service are essentially syntax sugars of provider, but they are used differently. It is suggested that you use service directly. It is easier to use ES6 class, and it will be easier to smoothly migrate to angular2. At the same time, it can avoid confusion when team members choose service or factory.

What data needs to be formatted is processed with filter. For example, the status value is converted into a Chinese value, and the timestamp is converted into a time string.

Need common DOM operation, put it in the instruction to write. In addition, if you need to introduce the jQuery component, you can also write an instruction to put the jQuery component initialization code in.

The controller and the view are maintained according to the one-to-one relationship. The scope object is initialized in the controller and the method (behavior) is added on the scope to assign values to the ViewModel. All other procedures should not appear in the controller. There should be no code related to page display and interaction in controller. For example, displaying and hiding certain elements should be the responsibility of HTML templates or instructions. The thinner the code, the better.

Global constant values are placed in constant.

Directive magic

It is not too much to use the word “magic” to describe the characteristic of command.

Pain point: in a word, instructions provide a set of methods and conventions of front-end componentization, which makes it more convenient to write and use UI components. Compared with jQuery, it solves the following pain points:

After generating HTML elements dynamically, you don’t need to add JavaScript features to them manually. For example: the native HTML checkbox is ugly. In the jQuery era, you can replace the checkbox with a custom effect. If there is a checkbox at the beginning of the page, we can call the initialization method of the custom checkbox at document.ready. However, if the check box is generated dynamically, we have to call the initialization method of the check box in every place where the check box is generated dynamically, which is quite troublesome. However, if the angularjs instruction is used, there will be no such problem. As long as the instruction is added to the chceckbox of the template, no matter whether the template is dynamic or static, there is no need to call the initialization method one by one through the business code. What is presented to the user is already the check box effect after the replacement of angularjs.

HTML and JavaScript of a component are integrated, not fragmented (besides, react is better than angular1. X). The introduction method of UI component based on jQuery is often like this. First, you are required to copy a specified HTML, and then call the initialization method. Instructions support the definition of corresponding template HTML. When importing, users may only need to write an instruction tag to automatically generate N lines of HTML and bind the corresponding JavaScript effect. Of course, in theory, jQuery can do the same, but it will be much more troublesome than the implementation of angular.

It is convenient and intuitive to apply and remove UI features. Suppose there is a need to add an input limit to an ordinary input box. You can only input specific characters (such as alphanumeric characters) and write the corresponding instruction. As long as you add the instruction label to the input box, you can immediately apply this feature. If you want to remove it later, you just need to remove the label. In contrast, jQuery is more troublesome. In jQuery, JavaScript effects are usually bound by element selectors. Therefore, when adding this feature, you need to consider specifying an appropriate element selector for the corresponding input box. When removing features, you need to consider: it is possible that you have added these initialization codes to the places where the input box is generated dynamically, and the JavaScript needs to be removed; If the element selector uses class, we have to consider whether other input boxes also have this class. If so, removing the code will also affect other input boxes.

skill

If you have to introduce a jQuery component, you can write an instruction to wrap it up and initialize the component in the instruction.

Note that the value in the require parameter is humpy. In HTML, it has to be converted to the corresponding middle dash name. For example, if there is a require parameter phonekey, then in HTML, it should be phone key = “XXX”. Although the truth is very simple, it is often wrong if you are not careful, and then you find that you can’t get the require parameter inside the instruction.

If you add elm. Bind (‘click ‘) to the link, when the value of the scope changes in the click callback function, remember to call scope. $apply(), otherwise the value change will not take effect.

Document and directory agreement
directory structure

The third-party library, CSS, picture to which directory, is not in the scope of this article, here omitted. What needs further explanation is the business code directory.

Picture description
We put the JavaScript and HTML templates of the system itself in the pages directory, and the common subdirectory places the common JavaScript and templates; Other subdirectories, take the function module name as the directory name, and then put the JavaScript and templates related to this module in it. In this way, when developing the same module function, it is convenient to switch between HTML and corresponding JavaScript. Some code specifications may also suggest creating different subdirectories under the module level directory according to several major components of angularjs, such as controller, filter, service, etc., such as module a / controller, module a / service, etc., while we put all JavaScript and HTML at the same level for several reasons

In our project, there are only about ten files per module on average. In particular, each module generally has only one filter and service. It seems unnecessary to create a directory for this file.

Through the file name, it is very convenient to distinguish different JavaScript component types and HTML template types. At the same time, because ide generally sorts by the letter of the file name, JavaScript and HTML with the same function will be next to each other, so it is much more convenient to find the corresponding template or JavaScript code.

File name Convention

This agreement is particularly important for angular. The specific agreement is as follows:
Picture description
For example, to develop the function of “create firewall” for the “firewall” module, its controller, the corresponding JavaScript is: firewall.create.ctrl.js, and the corresponding HTML template is:

firewall.create.ctrl.HTML。 For the convenience of writing the file name, the abbreviation of the component is defined: controller > Ctrl; factory,service -> svr ; filter-> fil ; directive -> dire。

There are two obvious advantages of such an agreement:

Through the file name, you can know the corresponding module, angularjs basic component type, template HTML or JavaScript.

JavaScript and HTML with the same function will be next to each other (if IDE is sorted by file name).

Communicate with back end server
According to the background interface specification, combined with the ability of angularjs, we do some encapsulation to make the interface request logic very simple. For specific analysis, let’s first look at the standard response format of our back-end interface. The front end will make some customization according to the return format of this interface

{

errno:0,
errmsg:"",
data:[
   {
        id:"test",
        name:"test"    
    }
]

}
The standard response of our project server is a JSON, which describes the result code of the request through errno, describes the cause of the error through errmsg (if the request is wrong), and returns normal data through data.

Error handling

When the interface returns errno= 0, indicating that the interface returns an exception (system exception or user input error). At this time, we hope to pop up the box to prompt the user that there is an error. Obviously, it will be very cumbersome to write this logic in every interface request logic. Fortunately, angularjs provides the function of interceptor. As long as we write an interceptor, we can handle all exception returns uniformly. First, we need to explicitly throw an HTTP error. Because when the background logic is wrong or the user’s input parameters are wrong, the returned HTTP status code is 200 (this is only the agreement of our project). Angularjs does not think that 200 is an error. Therefore, we need to do some small actions.

$httpProvider.defaults.transformResponse.push(function (responseData) {

        if (responseData.errno != 0) {
            throw responseData;
        }
        ……
  });

The $httpprovider.defaults.transformresponse.push (hereinafter referred to as transformresponse) function of angularjs can handle all HTTP responses in a unified way. Here, we capture all errno through it= 0 and throw an exception out. Next, we need to catch this exception in $httpprovider.interceptors and pop up the box. The code is as follows:

$httpProvider.interceptors.push(function () {
return {

responseError: function (response) {
   if (response) {
      if (response.hasOwnProperty("errmsg")) {
          if (response.errno > 0) {
                            alert(response.errmsg);
        } else {
         Alert ("system maintenance, please try again later");
        }

     }
     else {
        if (response.status == 404) {
           Alert ("sorry, background service error, unable to find the corresponding interface");
        }
        else {
            Alert ("sorry, background service error");
        }
      }
    }
   }
 }
});

Some may ask, why not write error handling logic directly in the initial transformresponse function? This is because, when the interface is normal, angularjs will call the transformresponse function in turn, and then call the responseerror of interceptors. However, in some exceptional cases, the transformresponse logic will not be called. For example, when the URL does not exist, the web container will return 404 pages by default, or when the program makes an error and the system code does not handle the error, the web container will return 500 pages by default. At this time, it will directly enter the responseerror of interceptors. Therefore, in order to cover all exceptions, it is necessary to throw an exception in transformresponse, and then handle it uniformly by responseerror.

Response content formatting

Because the data concerned by the front end is placed in the data attribute of the response content. The other two attributes, errno and errmsg, do not care when normal data is returned. In order to facilitate data retrieval, we can further optimize the processing in transformresponse. When errno = = 0, responsedata.data is returned. In this way, data can be used directly in business logic instead of xxx.data.

$httpProvider.defaults.transformResponse.push(function (responseData) {

    if (responseData && responseData.hasOwnProperty("errno") && responseData.hasOwnProperty("errmsg") && responseData.hasOwnProperty("data")) {
        if (responseData.errno == 0) {
            if (Angular.isArray(responseData.data) || Angular.isObject(responseData.data)) {
                return responseData.data;
            } else {
                return responseData
            }
        }
        else {
            throw responseData;
        }
    }
    else {
        return responseData;
    }
});

Further encapsulating $resource

Each module in the project creates the corresponding service file to communicate with the background. For example, the “hard disk” module corresponds to disksvr, and the “firewall” module corresponds to firewallsvr. After this division, it will be very convenient to find all the background request logic of the front end.

When encapsulating the background communication logic, we use $resource, which is a component of angularjs itself. When your background interface meets the restful specification, you can easily use resource to communicate with the background. As our background is not a complete restful implementation, we need to do some simple encapsulation. The schematic code is as follows:

app.factory(“DiskSvr”, function () {

var url = "schedule/disk";

var customAPI = {
    clone: {
        method: "post"
    }
}

return getApi(url, customAPI);

});

//It’s public. Every SVR can use it
getApi = function (path, customAPI) {

Angular.forEach(customAPI, function (value, key) {
    if (!value.url) {
        value.url = util.connectPath(path, key);
    } else {
        value.url = util.connectPath(path, value.url);
    }
});

//All of them
var defaultAPI = {
    detail: {
        url: baseUrl + "/get"
    },
    delete: {
        url: baseUrl + "/delete",
        method: "delete"
    },
    create: {
        url: baseUrl + "/create",
        method: "post"
    },
    update: {
        url: baseUrl + "/update",
        method: "put"
    }
}

return $resource(path, {}, Angular.extend(defaultAPI, customAPI));

}
The above code mainly does these things:

For all SVR, it injects four interfaces which are necessary for each module interface, the most basic of which is to add, delete and query. This eliminates the need to add these interfaces to each SVR. For example, calling DiskSvr.create () in business logic will call the schedule/disk/create interface with post request.

The configuration of new interface is simplified. Add an interface, just configure the corresponding HTTP method and name.

By referencing $resource components, further encapsulation and SVR files, you will not see any basic code for background communication in business Controller. If you need to communicate with the background in this Controller, you just need to inject the response SVR and then call the corresponding method.

Injection request header

In our project, it is agreed that all requests should carry the same information in the header, which is very simple for angularjs: after executing the following code, all HTTP requests will carry the header information named Project:

Picture description

Communication between controllers
Controller call

Suppose that controller a wants to call a function of controller B to tell the companion controller that something you care about has changed. What should I do? For example, in a specific business scenario, there are two controllers, one is the home page controller, and there is a pop-up form on the home page. This pop-up form also has a controller. After the user successfully submits this form, the pop-up controller needs to tell the home page controller, “brother, please update some data on the home page.”. The recommended approach is to use angularjs message mechanism. For example, in the above example, the pop-up controller is a child controller of the home controller. Then the pop-up controller can bubble up to deliver messages:

Picture description
The parent controller captures this message:

Picture description

This is a good decoupling method. Assuming that the two controllers are responsible for two developers, then I develop my controller and you develop yours. I don’t have to care about your logic.

data sharing

How to share data among multiple controllers? The simplest but not recommended method is to plug the data into the rootscope. However, this is just like the global variable of JavaScript, which is not easy to control. Here we recommend our approach: write a shared data facty for storing and setting shared data. Set method is defined in it. All shared variables need to be set through set method. Then the data is obtained through the data variable. The pseudo code is as follows:

app.factory(‘ShareSvr’, function(){

var shareData = {
    peopleNum
}
return {
    DATA:shareData,
    setPepleNum:function(num){
        shareData.peopleNum = num;
    }
}

});

app.controller(‘TestController’,function(ShareSvr){

var self = this;
this.DATA = ShareSvr.DATA;

}
It is not required here that the data value can only be obtained through the get method, so that it is convenient to take the value in the View HTML corresponding to the controller later.

Third party library / Resource Recommendation
UI Router
Routing, a characteristic that almost all MVC frameworks should have, is an essential part of front-end single page application (SPA). Compared with the native ngrouter, UI router is more powerful, with multi view, nested routing and other features, which can solve most of the application scenarios of routing.

ngDialog
A pop-up control, powerful, I prefer the place is that it does not write dead pop-up HTML, you can easily define the pop-up template you want. For example, our project has made two kinds of pop-up frames through it, one is the ordinary pop-up frame, the other is the side pull frame (sliding out from the right side of the screen, occupying the height of the browser, and the width accounts for half of the screen (or other custom width).

Angularjs code specification
https://github.com/johnpapa/angular-styl…
https://github.com/mgechev/Angularjs-sty…

Angular-filter
Provides a lot of practical filter, string class, math class, collection class and so on.

w5c-validator
Based on the original form verification of angular.js, unified verification rules and prompt information, expanded some error prompt functions on the original basis, so that you don’t need to write some prompt information template on each form, and concentrate on realizing business logic, which is produced by Chinese.

ng-table
Lightweight, powerful form component. You can easily modify the style and interaction effect of the table.

Original linkhttp://geek.csdn.net/news/detail/67432

Recommended Today

Looking for frustration 1.0

I believe you have a basic understanding of trust in yesterday’s article. Today we will give a complete introduction to trust. Why choose rust It’s a language that gives everyone the ability to build reliable and efficient software. You can’t write unsafe code here (unsafe block is not in the scope of discussion). Most of […]