# Struktur Controller AMN

Terdapat dua buah controller untuk setiap project, yaitu controller yang menghasilkan halaman HTML dan controller yang menghasilkan JSON. Controller yang menghasilkan JSON akan diakses oleh controller lain ketika dibutuhkan pengambilan data dengan memanfaatkan Ajax.

## Struktur Class Controller

Class controller terdapat di dalam project ASP.NET MVC. Ada beberapa penyesuaian agar controller ini kompatibel dengan framework AMN, diataranya adalah:

* Pada project MVC, perlu dilakukan reference terhadap beberapa file .dll framework sebagai berikut

> - Arcanet.Common.dll
> - CoreInterfaceBase.dll
> - CoreMVCBase.dll
> - CoreServiceBase.dll

* Namespace controller perlu diubah menjadi CoreApp.Controllers
* Class controller perlu dideklarasikan sebagai PartialClass dan diturunkan dari CoreMVCBase.Controllers

```csharp
public partial class ItemController : CoreMVCBase.BaseController {
}
```

* Perlu mengimplementasikan method *LoadDefaultValue()* dan *LoadService()*. Kedua method ini akan secara otomatis dipanggil ketika controller di-instantiate.

```csharp
protected override void LoadDefaultValue() {
	// diisi dengan inisialisasi function id untuk proses validasi hak akses
}

protected override void LoadService() {
	// membuat instance dari service
	mItemService = ConstructorInjection.CreateInstance<IItemService>("Service.xml", "service");
}
```

## Method Publik Standar

Sebuah controller akan memiliki 10 method standar yang dapat diakses secara publik, yaitu:

#### Method Index()

```csharp
public ActionResult Index() {}

[HttpPost]
public ActionResult Index(FormCollection param) {}
```

Kedua method ini digunakan untuk menampilkan halaman index. Pada halaman index, terdapat *filter* untuk pencarian data dan juga *paging* apabila jumlah baris data yang ditampilkan lebih dari 10 (*default*). Contoh isi dari method index adalah sebagai berikut:

```csharp
public override ActionResult Index()
{
	FormCollection param = ToFormCollection(Request.QueryString);
	base.Index();
	return Index(param);
}

[HttpPost]
public override ActionResult Index(FormCollection param) 
{
	PrepareDebugMode();
	if (!base.IsLogin()) { return RedirectToAction(_LOGIN_ACTION, _LOGIN_CONTROLLER); }
	if (!base.IsAllowRead()) { return RedirectToAction(_ACCESS_DENIED_ACTION, _ACCESS_DENIED_CONTROLLER); }
	
	SetLabels();	
	base.Index(param);
	RestorePreviousModelState();

	/*FILTER*/
	/*---------------------------------*/
	ItemFilter itemFilter = new ItemFilter();
	itemFilter = GenerateFilter(param);
	ViewBag.Index = true;
	/*---------------------------------*/

	/*DATA PAGING*/
	/*----------------------------------*/
	mItemService.UserInfo = base.UserInformation;
	int page = 0;
	int limit = 0;
	int offset = 0;
	base.GetPageLimitOffset(ref page, ref limit, ref offset, param, _ROW_PER_PAGE);
	/*----------------------------------*/

	/*DATA*/
	/*----------------------------------*/
	var items = mItemService.GetItemsDt(itemFilter, limit, offset);
	if (items == null) { items = new DataTable(); }
	/*----------------------------------*/

	/*GENERATE PAGING*/
	/*----------------------------------*/
	long total_row = mItemService.GetItemCount(itemFilter);
	ViewBag._PAGING = GeneratePagingLink("Item", "Index", total_row, param);
	ViewBag.Offset = offset;
	/*----------------------------------*/

	/*GENERATE COMBO*/
	/*----------------------------------*/

	return View("Index", items);
}
```

#### Method Create()

```csharp
public ActionResult Create() {}

[HttpPost]
public ActionResult Create(FormCollection param) {}
```

Kedua method ini digunakan untuk menampilkan halaman create. File .cshtml yang digunakan bernama input.cshtml dan digunakan bersama baik oleh method *Create()* maupun method *Edit()*. Apabila proses create berhasil, maka akan ditampilkan halaman detail, tetapi jika proses create gagal, maka data akan ditampilkan kembali di halaman create dan informasi error ditampilkan dibagian atas layar. Contoh isi dari method Create adalah sebagai berikut:

```csharp
public override ActionResult Create()
{
	PrepareDebugMode();
	if (!base.IsLogin()) { return RedirectToAction(_LOGIN_ACTION, _LOGIN_CONTROLLER); }
	if (!base.IsAllowCreate()) { return RedirectToAction(_ACCESS_DENIED_ACTION, _ACCESS_DENIED_CONTROLLER); }
	base.Create();
	SetLabels();

	Item NewItem = new Item();
	NewItem.Price = 0;
	return CreateInputView(NewItem);
}

[HttpPost]
public override ActionResult Create(FormCollection param)
{
	PrepareDebugMode();
	if (!base.IsLogin()) { return RedirectToAction(_LOGIN_ACTION, _LOGIN_CONTROLLER); }
	if (!base.IsAllowCreate()) { return RedirectToAction(_ACCESS_DENIED_ACTION, _ACCESS_DENIED_CONTROLLER); }
	SetLabels();

	Item item = BindAllDataToObject(param);
	try
	{
		base.Create(param);
		if (!ValidateInput(param))
		{
			return CreateInputView(item);
		}
		if (InsertItem(ref item))
		{
			return RedirectToAction("Detail/" + item.Id);
		} else {
			return CreateInputView(item);
		}
	}
	catch (Exception ex)
	{
		ModelState.AddModelError("ErrException", ex.Message);
		base.Create(param);
		return CreateInputView(item);
	}
}
```

#### Method Edit()

```csharp
public ActionResult Edit(String id) {}

[HttpPost]
public ActionResult Edit(String id, FormCollection param) {}
```

Kedua method ini digunakan untuk menampilkan halaman edit. Sama seperti create, halaman ini menggunakan view input.cshtml. Ketika proses edit berhasil, maka akan ditampilkan halaman detail, dan jika prosesnya gagal, maka data akan ditampilkan kembali pada halaman edit dan informasi error ditampilkan pada bagian atas layar. Contoh isi dari method Edit adalah sebagai berikut:

```csharp
public override ActionResult Edit(string id)
{
	PrepareDebugMode();
	if (!base.IsLogin()) { return RedirectToAction(_LOGIN_ACTION, _LOGIN_CONTROLLER); }
	if (!IsAllowUpdate()) { return RedirectToAction(_ACCESS_DENIED_ACTION, _ACCESS_DENIED_CONTROLLER); }
	SetLabels();

	try
	{
	base.Edit(id);
		mItemService.UserInfo = base.UserInformation;
		Item item = mItemService.GetItem(id);

		String companyId = (item != null ? item.CompanyId : "");
		if (!IsOwnCompany(companyId))
		{
			ModelState.AddModelError("errNotAllowed", "You don't have permission to edit this data");
			this.SaveModelStateToTempData();
			return RedirectToAction("Index");
		}

		if (item == null) { return RedirectToAction("Index"); }
		return CreateInputView(item);
	}
	catch(Exception ex)
	{
		ModelState.AddModelError("ErrException", ex.Message);
		SaveModelStateToTempData();
		base.Edit(id);
		return RedirectToAction("Index");
	}
}

[HttpPost]
public override ActionResult Edit(string id, FormCollection param) {
	PrepareDebugMode();
	if (!base.IsLogin()) { return RedirectToAction(_LOGIN_ACTION, _LOGIN_CONTROLLER); }
	if (!IsAllowUpdate()) { return RedirectToAction(_ACCESS_DENIED_ACTION, _ACCESS_DENIED_CONTROLLER); }
	SetLabels();

	Item item = BindAllDataToObject(param);
	Item oldItem = mItemService.GetItem(id);
	String companyId = (oldItem != null ? oldItem.CompanyId : "");
	if (!IsOwnCompany(companyId))
	{
		ModelState.AddModelError("errNotAllowed", "You don't have permission to edit this data");
		this.SaveModelStateToTempData();
		return RedirectToAction("Index");
	}

	item.CreatedBy = oldItem.CreatedBy;
	item.CreatedDate = oldItem.CreatedDate;
	item.LastUpdatedBy = base.UserInformation;
	item.LastUpdatedDate = DateTime.Now;

	try
	{
		base.Edit(id, param);
		string old_id = id;
		if (!ValidateInput(param))
		{
			return CreateInputView(item);
		}

		if (UpdateItem(id, item))
		{
			return RedirectToAction("Detail/" + item.Id);
		} else {
			return CreateInputView(item);
		}
	}
	catch(Exception ex)
	{
		ModelState.AddModelError("ErrException", ex.Message);
		base.Edit(id, param);
		return CreateInputView(item);
	}
}
```

#### Method Detail()

```csharp
public ActionResult Detail(String id) {}
```

Method ini digunakan untuk menampilkan halaman detail dari suatu data. Apabila data tidak ditemukan, maka akan di redirect ke halaman index dan diinformasikan bahwa data tidak ditemukan. Contoh isi dari method Detail adalah sebagai berikut:

```csharp
public override ActionResult Detail(string id)
{
	PrepareDebugMode();
	if (!base.IsLogin()) { return RedirectToAction(_LOGIN_ACTION, _LOGIN_CONTROLLER); }
	if (!base.IsAllowRead()) { return RedirectToAction(_ACCESS_DENIED_ACTION, _ACCESS_DENIED_CONTROLLER); }
	SetLabels();

	try
	{
		base.Detail(id);
		mItemService.UserInfo = base.UserInformation;
		Item item = mItemService.GetItem(id);
		if (item == null) { return RedirectToAction("Index"); } 

		String companyId = (item != null ? item.CompanyId : "");
		if (!IsOwnCompany(companyId))
		{
			ModelState.AddModelError("errNotAllowed", "You don't have permission to view this data");
			this.SaveModelStateToTempData();
			return RedirectToAction("Index");
		}

		return View(item);
	}
	catch(Exception ex)
	{
		ModelState.AddModelError("ErrException", ex.Message);
		SaveModelStateToTempData();
		base.Detail(id);
		return RedirectToAction("Index");
	}
}
```

#### Method Delete()

```csharp
public ActionResult Delete(String id) {}
```

Method ini digunakan untuk melakukan proses delete data. Proses delete berhasil maupun gagal, aplikasi akan tetap kembali ke halaman index. Bedanya, jika terjadi error ketika proses delete dilakukan, informasi error ini akan ditampilkan pada bagian atas layar di halaman index. Contoh isi dari method Delete adalah sebagai berikut:

```csharp
public override ActionResult Delete(string id) 
{
	PrepareDebugMode();
	if (!base.IsLogin()) { return RedirectToAction(_LOGIN_ACTION, _LOGIN_CONTROLLER); }
	if (!base.IsAllowDelete()) { return RedirectToAction(_ACCESS_DENIED_ACTION, _ACCESS_DENIED_CONTROLLER); }
	SetLabels();

	try
	{
		base.Delete(id);
		mItemService.UserInfo = base.UserInformation;
		Item item = mItemService.GetItem(id);
		if (item == null) { return RedirectToAction("Index"); } 

		String companyId = (item != null ? item.CompanyId : "");
		if (!IsOwnCompany(companyId))
		{
			ModelState.AddModelError("errNotAllowed", "You don't have permission to delete this data");
			this.SaveModelStateToTempData();
			return RedirectToAction("Index");
		}

		mItemService.DeleteItem(id);
		return RedirectToAction("Index");
	}
	catch(Exception ex)
	{
		ModelState.AddModelError("ErrException", ex.Message);
		SaveModelStateToTempData();
		base.Delete(id);
		return RedirectToAction("Index");
	}
}
```

#### Method Popup()

```csharp
public ActionResult PopUp() {}

[HttpPost]
public ActionResult PopUp(FormCollection param) {}
```

Method ini digunakan untuk menampilkan halaman list dalam bentuk popup. Cara kerjanya sama dengan halaman index, perbedaannya adalah pada halaman popup ini, bisa ditambahkan tombol pilih maupun checkbox untuk memilih secara masal. Contoh isi dari method Popup adalah sebagai berikut:

```csharp
public ActionResult PopUp()
{
	FormCollection param = ToFormCollection(Request.QueryString);
	return PopUp(param);
}

[HttpPost]
public ActionResult PopUp(FormCollection param)
{
	PrepareDebugMode();
	if (!base.IsLogin()) { return RedirectToAction(_LOGIN_ACTION, _LOGIN_CONTROLLER); }
	if (!base.IsAllowRead()) { return RedirectToAction(_ACCESS_DENIED_POPUP_ACTION, _ACCESS_DENIED_CONTROLLER); }
	SetLabels();

	//FILTER
	/*---------------------------------*/
	ItemFilter itemFilter  = GenerateFilter(param);
	/*---------------------------------*/
	//PAGING
	/*---------------------------------*/
	int page = (String.IsNullOrEmpty(param["page"]) ? 1 : Convert.ToInt32(param["page"]));
	int limit = (String.IsNullOrEmpty(param["rowperpage"]) ? _ROW_PER_PAGE : Convert.ToInt32(param["rowperpage"]));
	int offset = (page - 1) * limit;
	base.GetPageLimitOffset(ref page, ref limit, ref offset, param, _ROW_PER_PAGE);
	mItemService.UserInfo = base.UserInformation;
	long total_row = mItemService.GetItemCount(itemFilter);
	ViewBag._PAGING = GeneratePagingLink("Item", "PopUp", total_row, param);
	/*---------------------------------*/

	/*DATA*/
	/*----------------------------------*/
	var items = mItemService.GetItemsDt(itemFilter, limit, offset);
	if (items == null) { items = new DataTable(); }
	/*----------------------------------*/

	/*COMBOBOX*/
	/*----------------------------------*/

	ViewBag.window_id = (param["window_id"] != null &&  !String.IsNullOrEmpty(param["window_id"]) ?  param["window_id"] : Request.QueryString["window_id"]);
	ViewBag.table_id = (param["table_id"] != null && !String.IsNullOrEmpty(param["table_id"]) ? param["table_id"] : Request.QueryString["table_id"]);
	ViewBag.multiselect = (param["multiselect"] != null && !String.IsNullOrEmpty(param["multiselect"]) ? param["multiselect"] : Request.QueryString["multiselect"]);
	return View(items);
}
```

## Private Methods

Untuk membantu proses pada controller, terdapat beberapa private methods, diantaranya adalah:

#### Method GenerateFilter()

```csharp
private ItemFilter GenerateFilter(FormCollection param) {}
```

Method ini digunakan untuk membuat object Filter. Object ini akan dikirimkan ke service layer untuk mem-filter data yang diambil untuk halaman list di index maupun di popup. Contoh isi dari method GenerateFilter adalah sebagai berikut:

```csharp
protected ItemFilter GenerateFilter(FormCollection param) {
    ItemFilter itemFilter = new ItemFilter(); 
    itemFilter.NameContains = base.GetViewDataFilter<string>("ItemName", param, null);
    itemFilter.Price = base.GetViewDataFilter<Nullable<int>>("ItemPrice", param, null);
    itemFilter.DescriptionContains = base.GetViewDataFilter<string>("ItemDescription", param, null);
    return itemFilter;
}
```

#### Method ValidateInput()

```csharp
private bool ValidateInput(FormCollection param) {}
```

Method ini digunakan untuk mem-validasi input pada controller. Sebagai contoh, jika input berupa integer, sedangkan data yang dikirim bukan berupa integer, maka akan terkena validasi dan menampilkan informasi error. Contoh isi dari method ValidateInput adalah sebagai berikut:

```csharp
private bool ValidateInput(FormCollection param) { 
    // TODO: customize validation here 
    return ModelState.IsValid;
}
```

#### Method BindDataToObject()

```csharp
private Item BindDataToObject(FormCollection param) {}
```

Method ini digunakan untuk melakukan binding data ke dalam model. Contoh isi dari method BindDataToObject adalah sebagai berikut:

```csharp
private Item BindDataToObject(FormCollection param) { 
	if (param == null) { return null; }

	DateTime parsedDate = DateTime.Now; 
	Item item = new Item(); 
	item.Id = param["Id"];
	item.Name = param["Name"];
	if (!string.IsNullOrEmpty(param["Price"]) && IsNumeric(param["Price"])) {
		item.Price = Convert.ToInt32(param["Price"]);
	}
	item.Description = param["Description"];
	return item;
}

private Item BindAllDataToObject(FormCollection param) {
	if (param == null) { return null; }
	Item item = BindDataToObject(param);
	return item;
}
```

#### Method CreateInputView()

```csharp
private ActionResult CreateInputView(Item item, string old_id = "", string masterPageName = "", string inputPageName = "input") {}
```

Method ini digunakan untuk me-load informasi tambahan seperti list at combobox pada halaman Create dan Edit. Contoh isi dari method CreateInputView adalah sebagai berikut:

```csharp
private ActionResult CreateInputView(Item item , string old_id = "", string masterPageName = "", string inputPageName = "Input") {
	// TODO: add additional action before showing the input page
	if (string.IsNullOrEmpty(masterPageName)) {
		return View(inputPageName, item);
	} else {
		return View(inputPageName, masterPageName, item);
	}
}
```

#### Method PrepareDebugMode()

```csharp
private void PrepareDebugMode() {}
```

Method ini dijalankan untuk melakukan proses login apabila project dicompile dengan mode Debug. Contoh isi dari method PrepareDebugMode adalah sebagai berikut:

```csharp
private void PrepareDebugMode() {
#if DEBUG
    base.Login(_DEBUG_USER_ID, _DEBUG_USER_PASSWORD);
#endif
}
```

## Konfigurasi Web.Config

Web.Config perlu di konfigurasi agar aplikasi dapat berjalan, yaitu dengan menambahkan owin:AutomaticAppStartup menjadi false.

```markup
<configuration>
  <appSettings>
    <add key="owin:AutomaticAppStartup" value="false" />
  </appSettings>
</configuration>
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://amn.gitbook.io/training/framework-amn-1/struktur-controller-amn.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
