본문 바로가기

Archive/ABAP

Simple Exercise on OData and SAP UI5 Application for the basic CRUD Operation

728x90

해당 내용의 원본 블로그 입니다.

https://blogs.sap.com/2015/02/05/simple-exercise-on-odata-and-sap-ui5-application-for-the-basic-crud-operation/

 

Simple Exercise on OData and SAP UI5 Application for the basic CRUD Operation | SAP Blogs

68 Likes 143,423 Views 67 Comments

blogs.sap.com

Contents

1. Introduction

2. Creation of OData Service

2.1 Creation of OData Service

2.1.1 Pre-requisites

2.1.2 Creation of Data Model

2.1.3 Generate Run time Objects

2.1.4 Service Implementation.

2.1.5 Registration of Service.

 

3.Consumption of OData Service SAP UI5 application.

3.1 Consuming OData Service for CRUD operation using UI5 Application.

3.1.1 Pre-requisites

3.1.2 Creation of SAP UI5 Application.

3.1.3 Create a Dashboard for Employee details display.

3.1.4 Pop-up window for Data Modify.

3.1.5 Create button for Data Creation.

4.Source code used.

List of Abbreviations/Acronyms

Term

Explanation

DPC

Data Provider Class

MPC

Model Provider Class

CRUD

Create, Read, Update, Delete

 

1. Introduction

In the demo we are going to see an example on SAP UI5 application for CRUD operation using SAP UI5 and OData Operations. This demo can be split in to two main divisions.

  • Creation of OData Service
  • Consuming an OData Service using SAP UI5 application.

2. Creation of OData Service

We are going to create an OData service for the custom Employee Details with the basic operations Create, Read, Delete and Update (CRUD).

2.1 Creation of OData Service

In this example we are going to see the creation of OData service using the SAP Gateway Service Builder(SEGW). The OData Service can also be built by various ways available and we took the Gateway Service Builder approach here. In this example, the OData has been deployed in the embedded deployment model where the single system acts as both Gateway and Backend system.

The below mentioned are the steps are used to create the OData service.

  • Pre-requisite
  • Creation of Data Model
  • Generate Runtime Objects
  • Registration of Service
  • Service Implementation

2.1.1 Pre-requisites

In the pre-requisite, we are going to create a transparent table which is going to act as a back end table and function module for the Create, Read, Update and Delete (CRUD) operation to be used in this OData creation.

1. Creation of Table, we will create a Z table with basic employee details. Once the table is created, please create some entries. Later, these entries will be displayed in the UI dashboard.

2. Create Function Modules for Create, Read, Update and Delete operation.

Important Note: Create the Function Module as Remote-Enabled Function Module.

  • Function Module for Create Operation

  • Function Module for Read Operation

  • Function Module for Update Operation

  • Function Module for Delete Operation

2.1.2 Creation of Data Model

The Data model is used to describe the OData Services which contains Entity, Properties, and EntitySet and so on.

In this example, we are going to build a OData service using SAP Gateway Service Builder (SEGW)

1. Go to Transaction Code: SEGW, which will land you in the Gateway Service Builder.

2. Create a Project

3. Once the Project is created, create Entity and EntitySet by the Import option available

4. Give the Entity, Structure and the EntitySet create option as below. We are going to use the already created employee table structure here.

5. Select all the Data source parameters and press Next.

6. Press Finish to complete the Data Model creation.

Note: There are number of ways to define the Data Model and we have chosen the import->DDIC structure option here.

7. The Entity and EntitySet are created now, and we can see the entity type has been created with reference to the given ABAP Structure.

8. Once it is added, we have the properties created now as below. Choose Empid as a Key here, the key also can be defined at the step 6 by marking the key field as “Is Key”.

2.1.3 Generate Run time Objects

1. Once all the above activities are completed, generate the run time objects by pressing the Ctrl+F3 button. This will pop up the below window with all the option to generate the run time objects.

2. Once this activity is complete, we have the objects generated as below.

This will generate the Data Provider Class, widely we call it as DPC and Model Provider Class and this we call it as MPC class.

DPC=> Business Logic for CRUD operation goes here.

MPC=>Data Model Class, where we can see the data model defined.

2.1.4 Service Implementation

In this section, we are going to generate the Business logic using the RFC Function Module we have created earlier for the CRUD Operations. Instead of using RFC Function Module, we can also use Business Object Repository. Also you can use your own custom logic in the DPC_EXT classes for the data operations.

1. Go to Service Implementation, select the Create Operation and go to Map to Data Source.

2. Select the Function Module for Create Option as below and press Continue

3. Then the below window will be open with the Data Source Parameter and the Data Mapping has to be done as follows. Also we have an option “Propose Mapping”, which does the data mapping automatically.

Create

Drag and drop the Data Source Parameter as shown below for the Mapping Operation.

Similarly, we have defined the mapping for Delete, GetEntity, GetEntitySet and Update operation using the Function Modules we have already created.

Delete

GetEntity

We have added one more Property, Empid to fetchthe records from the backend based on the Key.

GetEntitySet

We have added one more Property, Empid to fetch the records from the backend based on the Key.

Update

Once the mapping is completed, generate run time objects

( Ctrl+F3 ) and go to your Data Provider Class(DPC) to find the ABAP coding has been generated automatically.

Once the above all activities are completed, we have to register the OData service to consume them.

2.1.5 Registration of Service

1. Go to gateway and select the Gateway service. You will find the OData service is yet to be registered as the Registration status traffic light button is grey.

2. Select the Service and press the Register

button to complete the Registration of the OData service and press yes to continue.

3. Select the package assignment as local object and continue the process.

4. On press of continue will register the OData Service. You can also add the service using the Transaction code: /IWFND/MAINT_SERVICE.

5. Select the Gateway Client and this will launch you in the SAP Netweaver Gateway Client. This can also be accessed separately using the T-code : /IWFND/GW_CLIENT. Execute the uri: /sap/opu/odata/sap/ZMM_EMP_SRV/$metadata to test the OData service.

By this successful response, the OData service has been built and it is working fine. Further we will see, how can we consume the OData service using SAP UI5 application in the next chapter.

Metadata Uri: http://:/sap/opu/odata/sap/ZMM_EMP_SRV/$metadata : OData Service Metadata document can be accessed here.

Entity Set : http://:/sap/opu/odata/sap/ZMM_EMP_SRV/EmployeeSet : The Employee records can be accessed here.

Replace the tags , with your server and port details.

3. Consumption of OData Service using SAP UI5 application

In this example, we will create a SAPUI5 application for Employee and we will see the see how the OData service can be consumed for the basic CRUD operation. Whereas the below are the CRUD methods and their equivalent HTTP methods in the OData service.

3.1 Consuming OData Service for CRUD Operation using SAP UI5 Application

In this example, we are going to create a UI5 application by which we can consume the already created OData service for the basic CRUD operation.

This app contains the following details:

  • Pre-requisite
  • Creation of SAP UI5 Application
  • Create a Dashboard to display Employee Details
  • Popup window for Data modify operation
  • Create button for Data creation

3.1.1 Pre-requisites

We need an Eclipse IDE with version Kepler, Luna or any other latest version with the SAP UI5 plugin to develop the SAP UI5 application. If you want to know how the SAP UI5 development plugins can be added to eclipse, click here.

3.1.2 Creation of SAP UI5 Application

Create a New SAP UI5 project by the below path in the menu bar

1. Go to Menu bar : File->New->Other->SAP UI5 Application development(Application Project) and then press next.

 

2. Give the project name, with the option to create a Initial view. The view can be created separately as well.

 

3. Provide the view name and choose the Paradigm as JavaScript and press Finish.

 

4. By this, we have created an empty SAPUI5 project called “EmpCRUD” with view “EmpDetails” as below.

3.1.3 Create a Dashboard for Employee details display

1. Create a dashboard with the Table contains fields as Employee Id, Name, Address and Designation. Assign the same to the Page and return the page. (EmpDetails.view.js)

 

2. You can execute and view the dashboard the option “Web App Preview” as below. Also you can use the option “Run on Server”, but you need to install Tomcat / any other web server for the same.

 

3. Then it appears in the Eclipse as below. Further you can copy and paste the url in the chrome browser. You can use any browser, but I have used chrome as the debugging in bit easy.

 

4. In the browser, it appears as below. An empty Dashboard is built now.

 

5. Now the Dashboard is built, we will see how the data can be populated here. Go to “EmpDetails.controller.js” and in the onInit function, write the coding as below to fetch the data from server. This will do a OData call to fetch the data from the server in JSON format. This data further has to be bind to the table we built already. Replace the tags , with your respective server details.

ZMM_EMP_SRV: Name of the OData Service, we already built.

EmployeeSet: Name of the entity set name for employees.

 

6. The data binding happens as below.

 

Run the app again in the “Web App Preview” to see the data displayed in the dashboard as below. When prompted for login credentials to your backend server, provide your User id/Password credentials to continue.

7. When you run/execute the application, it may not run due to ‘Access-Control-Allow-Origin’ issue. To overcome this issue, we have an option available with chrome browser.

  • Make sure you close out all instances of Google Chrome including ending all its processes in the Windows Task Manager.
  • Right-Click on your Google Chrome Icon, Go to the Target: and add the command “–disable-web-security”, apply the settings as below.
  • This should allow you to overcome the ‘Access-Control-Allow-Origin’ issue and the application should run in the chrome browser.

8.When executed again, the employee data appears in the chrome browser as below.

 

By this READ (GET) operation is complete. Further we will see the remaining operations below.

3.1.4 Pop-up window for Data Modify

1. We will create a popup window with text areas for Employee Id, Emp Name, Address and Designation with three buttons Update, Delete and Cancel for the modify operations. Go to View.js and add the coding as below.

 

2. We have the dialog box or the pop-up window created now. Further we will see how we can make use of the dialog box for the modify operation.

On the press of employee details, we will open a dialog box where we will show the employee details and it can be modified or deleted. We will read the employee details from the selected item and will populate the dialog box. The key, “Employee id” we will disable for edit.

 

3. Once it is done, re -run the application to see a dialog window pop-up on a selection of the item. The selected record details are shown now. But the buttons “Update”, ”Delete” and “Cancel” actions are yet to be defined.

 

4. Go to the controller.js. where you can define the action of the Update button.

 

5. GET in the OData call is used to fetch the X-CSRF token. Once the token is fetched, it act as an authentication to do the further update(PUT) operation.

Note: You need a CSRF token to be fetched to perform the Modify/Insert operation. To know more about the CSRF token click here.

Re-run the application to test the update operation. I have updated the address and pressed update button.

6. On the success, we will reload the main page with the newly updated details.

 

7. On the press of Ok button, the page will be re-loaded with the newly updated details.

 

The update operation is complete. We will further see the Delete operation details.

8. Go to the controller.js. where we can define the action of the Delete button.

 

9. GET in the OData call is used to fetch the X-CSRF token. Once the token is fetched, it act as an authentication to do the further delete(DELETE) operation.

Note: You need a CSRF token to be fetched to perform the Modify/Insert operation. To know more about the CSRF token click here.

Re-run the application to test the delete operation. I have selected a record and pressed delete button.

10. On the success, we will reload the main page with the deleted details.

 

11. On the press of Ok button, the page will be re-loaded with the modified details.

 

12. The Delete operation is complete. On the press of the cancel button we will close the dialog box by adding the below in the controller.js

 

3.1.5 Create button for Data Creation

1. We will place a button “Create New Employee” in the dashboard and by this we will see the creation of a new employee.

Go to view.js where we will create button Submit, Save. We will add the Save button to the Dialog box.

 

2. We will add the button Submit button to the main dashboard as below.

 

3. Further we will go to controller.js to do the action for the “Create New Employee”. We will use the same dialog box, we have already created for the modify operation. We will hide the “Save” button in the modify operation and while creation, we will hide the “Update”, “Delete” button.

 

4. Re-run the application to see the new button “Create New Employee” visible in the main page and on the press of the button, a dialog box with the employee details can be added.

 

5. While press on the save, nothing will happen as we are yet to write the function for the “Save” action.

Go to the controller.js, where we can define the action for the “Save” button.

 

6. GET in the OData call is used to fetch the X-CSRF token. Once the token is fetched, it act as an authentication to do the further Create(POST) operation.

Note: You need a CSRF token to be fetched to perform the Modify/Insert operation. To know more about the CSRF token click here.

Re-run the application to test the Create operation.

7. On the success, we will reload the main page with the newly created details.

 

8. On the press of Ok button, the page has been re-loaded with the newly created details.

 

The Create operation is complete. By then we have completed all the basic CRUD operations.

4. Source code used

The source code used in this project is also given as a reference here.

Index.html

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv=“X-UA-Compatible” content=“IE=edge”>
        <meta http-equiv=‘Content-Type’ content=‘text/html;charset=UTF-8’/>
        <script src=“resources/sap-ui-core.js”
            id=“sap-ui-bootstrap”
            data-sap-ui-libs=“sap.m”
            data-sap-ui-theme=“sap_bluecrystal”>
        </script>
        <!– only load the mobile lib “sap.m” and the “sap_bluecrystal” theme –>
        <script>
            sap.ui.localResources(“empcrud”);
            var app = new sap.m.App({initialPage:“idEmpDetails1”});
            var page = sap.ui.view({id:“idEmpDetails1”, viewName:“empcrud.EmpDetails”,
            type:sap.ui.core.mvc.ViewType.JS});
            app.addPage(page);
            app.placeAt(“content”);
        </script>
    </head>
    <body class=“sapUiBody” role=“application”>
        <div id=“content”></div>
    </body>
</html>

EmpDetails.view.js

sap.ui.jsview(“empcrud.EmpDetails”, {
    /** Specifies the Controller belonging to this View.
    * In the case that it is not implemented, or that “null” is returned, 
    * this View does not have a Controller.
    * @memberOf empcrud.EmpDetails
    */
    getControllerName : function() {
                return “empcrud.EmpDetails”;
    },
    /** Is initially called once after the Controller has been instantiated. 
    * It is the place where the UI is constructed.
    * Since the Controller is given to this method, its event handlers 
    * can be attached right away.
    * @memberOf empcrud.EmpDetails
    */
    createContent : function(oController) {
		var oPage = new sap.m.Page({
			title: “Employee Details”,
		});
		var oBtnUpd = new sap.m.Button(“Update”, {
			text: “Update”,
			tap: [ oController.Update, oController ]
    });
    var oBtnDel = new sap.m.Button(“Delete”, {
		text: “Delete”,
		tap: [ oController.Delete, oController ]
		});                                 
    var oBtnCan = new sap.m.Button(“Cancel”, {
        text: “Cancel”,
        tap: [ oController.Cancel, oController ]
        });
    var oBtnSub = new sap.m.Button(“Submit”, {
        text: “Create New Employee”,
        press: oController.NewEntry, 
        });                 
    var oBtnSav = new sap.m.Button(“Save”, {
        text: “Save”,
        tap: [ oController.Save, oController ]
        });
// Dialog box / pop-up window for Add/Modify Employee Data              
	var oDialog = new sap.m.Dialog(“Dialog”,{
		title:“Add/Modify Employee”,
		modal: true,
		contentWidth:“1em”,
		content:[
		new sap.m.Label({text:“Enter Emp Id(must be a number)”}),
		new sap.m.Input({                                       
		maxLength: 20,
		id: “Id”
		}), 
		new sap.m.Label({text:“Enter Name”}),
		new sap.m.Input({                                       
		maxLength: 20,  
		id: “Name”
		}),
		new sap.m.Label({text:“Enter Address”}),
		new sap.m.Input({                                       
		maxLength: 20,  
		id: “Address”
		}),                                 
		new sap.m.Label({text:“Enter Designation”}),
		new sap.m.Input({                                       
		maxLength: 20,
		id: “Role”
		}),oBtnUpd, oBtnDel, oBtnCan, oBtnSav
		]
		});        
// Table or Dashboard to show the Employee Data                         
    var oTable = new sap.m.Table({
        id: “Employees”,
        itemPress : [ oController.ItemPress,oController ],
        columns: [
        new sap.m.Column({
        width: “1em”,
        header: new sap.m.Label({
        text: “Emp ID”  }) }),
        new sap.m.Column({
        width: “1em”, 
        header: new sap.m.Label({
        text: “Name” })
        }),
        new sap.m.Column({
        width: “1em”, 
        header: new sap.m.Label({
        text: “Address”
        })
        }),
        new sap.m.Column({   
        width: “1em”,
        header: new sap.m.Label({
        text: “Designation”
        })
        })
        ]  
    });
// Template  to map the data to the respective column       
var template = new sap.m.ColumnListItem({
    id: “first_template”,
    type: “Navigation”,
    visible: true,
    cells: [
    new sap.m.Label(“ID”, {
    text: “{Empid}”
        }),
    new sap.m.Label({
    text: “{Empname}”
            }),
    new sap.m.Label({
    text: “{Empadd}”
        }),
    new sap.m.Label({
    text: “{Empdes}”
    })
    ]       
 });
var  oFilters = null;
 oTable.bindItems( “/results”,template, null, oFilters);       
 oPage.addContent(oTable);
 oPage.addContent(oBtnSub);
	return oPage;               
		}
});

 

EmpDetails.controller.js

sap.ui.controller(“empcrud.EmpDetails”, {
/**
* Called when a controller is instantiated and its 
* View controls (if available) are already created.
* Can be used to modify the View before it is displayed, 
* to bind event handlers and do other one–time initialization.
* @memberOf empcrud.EmpDetails
*/
	onInit: function() {
		var sServiceUrl = “http://<host name>:<port no>/sap/opu
        /odata/sap/ZMM_EMP_SRV”;
		var oModel = new sap.ui.model.odata.ODataModel(sServiceUrl,true);
		var oJsonModel = new sap.ui.model.json.JSONModel();
oModel.read(“/EmployeeSet?”,null,null,true,function(oData,repsonse){
    oJsonModel.setData(oData);
});    
sap.ui.getCore().setModel(oJsonModel);
    },
/**
* Similar to onAfterRendering, but this hook is invoked before the controller’s 
* View is re–rendered
* (NOT before the first rendering! onInit() is used for that one!).
* @memberOf empcrud.EmpDetails
*/
//          onBeforeRendering: function() {
//
//          },
/**
* Called when the View has been rendered (so its HTML is part of the document).
* Post–rendering manipulations of the HTML could be done here.
* This hook is the same one that SAPUI5 controls get after being rendered.
* @memberOf empcrud.EmpDetails
*/
//          onAfterRendering: function() {
//
//          },
/**
* Called when the Controller is destroyed. Use this one to free resources and 
* finalize activities.
* @memberOf empcrud.EmpDetails
*/
//          onExit: function() {
//
//          }
	ItemPress: function(evt) {
		sap.ui.getCore().byId(“Dialog”).open();                     
		sap.ui.getCore().byId(“Update”).setVisible(true);
		sap.ui.getCore().byId(“Delete”).setVisible(true);
		var oSelectedItem = evt.getParameter(“listItem”);
		var sID = oSelectedItem.getBindingContext().getProperty(“Empid”);
		var sName = oSelectedItem.getBindingContext().getProperty(“Empname”);
		var sAddr = oSelectedItem.getBindingContext().getProperty(“Empadd”);
		var sRole = oSelectedItem.getBindingContext().getProperty(“Empdes”);
		sap.ui.getCore().byId(“Id”).setValue(sID);
		sap.ui.getCore().byId(“Name”).setValue(sName);
		sap.ui.getCore().byId(“Address”).setValue(sAddr);
		sap.ui.getCore().byId(“Role”).setValue(sRole);
		sap.ui.getCore().byId(“Id”).setEnabled(false);
		},
		NewEntry: function() {
		sap.ui.getCore().byId(“Dialog”).open();
		sap.ui.getCore().byId(“Save”).setVisible(true);
		sap.ui.getCore().byId(“Update”).setVisible(false);
		sap.ui.getCore().byId(“Delete”).setVisible(false);
		sap.ui.getCore().byId(“Id”).setValue(“”);
		sap.ui.getCore().byId(“Name”).setValue(“”);
		sap.ui.getCore().byId(“Address”).setValue(“”);
		sap.ui.getCore().byId(“Role”).setValue(“”);                 
		sap.ui.getCore().byId(“Id”).setEnabled(true);
		},                      
Save: function() {
	var oEntry = {};
	oEntry.Empid= sap.ui.getCore().byId(“Id”).getValue();
	oEntry.Empname= sap.ui.getCore().byId(“Name”).getValue();
	oEntry.Empadd= sap.ui.getCore().byId(“Address”).getValue();
	oEntry.Empdes= sap.ui.getCore().byId(“Role”).getValue();    
	OData.request({
		requestUri : “http://<host name>:<port no>/sap/opu/
        odata/sap/ZMM_EMP_SRV
        /EmployeeSet”,
		method : “GET”,
		headers : {
				“X-Requested-With” : “XMLHttpRequest”,
				“Content-Type” : “application/atom+xml”,
				“DataServiceVersion” : “2.0”,
				“X-CSRF-Token” : “Fetch”
				}
			},
			function(data, response) {
				header_xcsrf_token = response.
                headers[‘x-csrf-token’];
				var oHeaders = {
					“x-csrf-token” : header_xcsrf_token,
					‘Accept’ : ‘application/json’,
			};
		OData.request({
				requestUri : “http://<host name>:<port no>
                /sap/opu/odata/sap/
                ZMM_EMP_SRV/EmployeeSet”,
				method : “POST”,
				headers : oHeaders,
				data:oEntry
			},
				function(data,request) {
				alert(“Employee Created Successfully”);         
				location.reload(true);
			},          
				function(err) {
				alert(“Employee Creation Failed”);
			});
		}, function(err) {
			var request = err.request;
			var response = err.response;
			alert(“Error in Get — Request “ + request +
            ” Response “ + response);
			});                                               
Update: function() {
	var oEntry = {};
	oEntry.Empid= sap.ui.getCore().byId(“Id”).getValue();
	oEntry.Empname= sap.ui.getCore().byId(“Name”).getValue();
	oEntry.Empadd= sap.ui.getCore().byId(“Address”).getValue();
	oEntry.Empdes= sap.ui.getCore().byId(“Role”).getValue();    
	OData.request({
		requestUri : “http://<host name>:<port no>/sap/opu
        /odata/sap/ZMM_EMP_SRV/
        EmployeeSet”,
		method : “GET”,
		headers : {
				“X-Requested-With” : “XMLHttpRequest”,
				“Content-Type” : “application/atom+xml”,
				“DataServiceVersion” : “2.0”,
				“X-CSRF-Token” : “Fetch”
				}
			},
			function(data, response) {
				header_xcsrf_token = response.
                headers[‘x-csrf-token’];
				var oHeaders = {
					“x-csrf-token” : header_xcsrf_token,
					‘Accept’ : ‘application/json’,
			};
		OData.request({
				requestUri : “http://<host name>:<port no>
                /sap/opu/odata/sap/
                ZMM_EMP_SRV/EmployeeSet(‘”+oEntry.Empid+“‘)”,
				method : “PUT”,
				headers : oHeaders,
				data:oEntry
			},
				function(data,request) {
				alert(“Update Success”);            
				location.reload(true);
			},          
				function(err) {
				alert(“Update Failed”);
			});
		}, function(err) {
				var request = err.request;
				var response = err.response;
				alert(“Error in Get — Request “ + request +
                ” Response “ + response);
			});         
			},
// Delete Action                                            
Delete: function() {
            var oEntry = {};
            oEntry.Empid= sap.ui.getCore().byId(“Id”).getValue();
            OData.request({
				requestUri : “http://<host name>:<port no>/sap
                /opu/odata/sap/
                ZMM_EMP_SRV/EmployeeSet(‘” + oEntry.Empid + “‘)”,
				method : “GET”,
				headers : {
						“X-Requested-With” : “XMLHttpRequest”,
						“Content-Type” : “application/atom+xml”,
						“DataServiceVersion” : “2.0”,
						“X-CSRF-Token” : “Fetch”
						}
					},
					function(data, response) {
						header_xcsrf_token = response.
                        headers[‘x-csrf-token’];
						var oHeaders = {
							“x-csrf-token” : header_xcsrf_token,
							‘Accept’ : ‘application/json’,
					};
				OData.request({
						requestUri : “http://<host name>:<port no>
                        /sap/opu/odata/
                        sap/ZMM_EMP_SRV/EmployeeSet(‘”+oEntry.Empid+“‘)”,
						method : “DELETE”,
						headers : oHeaders,
						data:oEntry
					},
						function(data,request) {
						alert(“Delete Success”);            
						location.reload(true);
					},          
						function(err) {
						alert(“Delete Failed”);
					});
				}, function(err) {
						var request = err.request;
						var response = err.response;
						alert(“Error in Get — Request “ + request + ” 
                        Response “ + response);
					});         
				},
// Cancel Action                          
  Cancel:function() {
    sap.ui.getCore().byId(“Dialog”).close();
     }
})

 

 

728x90