본문 바로가기
ASP.NET MVC

ASP.NET MVC] Ajax with JSON - .getJSON(), client-side template(jquery.tmpl.js)

by Fastlane 2022. 7. 13.
728x90
반응형

JSON이란?

JSON은 JavaScript Object Notation을 나타내며, 간결하게 데이터를 표기한다. 

JavaScript eval 함수에 JSON string을 전달하여 object로 deserialize할 수 있다. 

 

Ajax with JSON

Index action에서 sites list를 보여주고, sitename을 클릭 시, JSON format의 site detail을 반환하는 Detail Action으로 Ajax를 request하자. 

Site.cs

    public class Site
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string PictureUrl { get; set; }
        public string Explanation { get; set; }
    }

SitesController.cs


    public class SitesController : Controller
    {
        private static List<Site> _sites = new List<Site>();


        public SitesController() {
            if (_sites.Count == 0)
            {
                _sites.Add(new Site { Id = 1, Name = "GitHub", PictureUrl = "https://github.githubassets.com/favicons/favicon.png", Explanation = "provider of Internet hosting for software development and version control using Git" });
                _sites.Add(new Site { Id = 2, Name = "Google", PictureUrl = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", Explanation = " American multinational technology company that focuses on artificial intelligence,[10] search engine technology, online advertising, cloud computing, computer software, quantum computing, e-commerce, and consumer electronics" });
            }
        }

        // GET: Speakers
        public ActionResult Index()
        {
            return View(_sites);
        }
        public ActionResult Details(int id)
        {
            var site = _sites.Where(s => s.Id == id).FirstOrDefault();
            return Json(site,
            JsonRequestBehavior.AllowGet);
            //JsonResult는 HTTP Post에서만 동작해야 하므로, GET request에 대한 response에서는 예외 옵션을 지정한다. 
        }
    }

Details action은 id를 parameter로 받아서 해당 site object를 찾아, JSON format으로 serialize 해서 반환한다. 

 

Index.cshtml

Client-side template은 server에서 전달받거나, JavaScript로 element를 구성하는 것 없이 markup을 생성하게 해준다. 

Client-side templating library는 여러가지 있는데, 여기서는 jQuery-tmpl을 사용해보자. 

하단에 jquery.tmpl.js파일을 포함시켰다. 

Template은 text/x-jquery-tmpl type의 script element안에 정의한다. JSON object property는 ${}로 감싼다. 

@model IEnumerable<AjaxExamples.Models.Site>
@{
    ViewBag.Title = "Index";
}

<h2>Sites</h2>

<ul class="sites">
    @foreach (var site in Model)
    {
        <li>
            @Html.ActionLink(site.Name, "Details", new { id = site.Id })
        </li>
    }
</ul>

<div class="selected-site" style="display:none">
</div>

<br style="clear:both" />

<script id="siteTemplate" type="text/x-jquery-tmpl">
    <img src="${PictureUrl}" alt="Speaker image" class="site-pic" />
    <p class="site-explain">
        ${Explanation}
    </p>
    <br style="clear:both;" />
</script>

@section scripts{

    <script src="~/Scripts/jquery.tmpl.js"></script>
    <script type="text/javascript" src="@Url.Content("~/scripts/Sites.js")"></script> 
}

Sites.js

        $(document).ready(function () {
            $("ul.sites a").click(function (e) {
                e.preventDefault();

                $(".selected-site").hide().html('');

                var url = $(this).attr('href');

                $.getJSON(url, null, function (site) {//url로 get request, 전달하는 데이터는 없으므로 null
                    //server에서 보내는 JSON string을 JavaScript object로 자동 serialize
                    $("#siteTemplate")
                        .tmpl(site)
                        .appendTo('.selected-site');
                    //template element를 찾은 후, template을 렌더링 하기 위해, tmpl 함수를 호출한다. 
                    //tempate은 selected-site element에 append된다. 
                    $('.selected-site').show();
                });
            });
        });

JavaScript가 정상동작 하지 않으면, 아래와 같이 JSON file이 보여진다. 

해당 이슈를 처리하기 위해, 아래와 같이 if를 추가한다. 

    public ActionResult Details(int id)
        {
            var site = _sites.Where(s => s.Id == id).FirstOrDefault();
            if (Request.IsAjaxRequest())
            {
                return Json(site,
                JsonRequestBehavior.AllowGet);
            }
            return View(site);
        }

JSON file이 아닌, 오류 화면이 보여진다. (Details View 파일이 없으므로)

if문을 사용하는 대신, Ajax와 Non-Ajax를 구분하기 위한 action method selector를 사용할 수 있다. 

CustomAttribute.cs

AcceptAjaxAttribute는 Ajax request일 때만 IsValidForRequest method를 통해 true를 반환한다. 

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AcceptAjaxAttribute : ActionMethodSelectorAttribute
    {
        public override bool IsValidForRequest(
        ControllerContext controllerContext, MethodInfo methodInfo)
        {
            return controllerContext.HttpContext
            .Request.IsAjaxRequest();
        }
    }

SitesController.cs

첫번째 action은 Ajax request일때만 실행된다. 

두번째는 non-Ajax request일때만 실행된다. Action name은 동일하게 설정할 수 없으므로 다르게 처리하고, Details URL로 접근할 수 있도록 ActionName attribute를 사용한다. 

        [AcceptAjax]
        public ActionResult Details(int id)
        {
            var site = _sites.Where(s => s.Id == id).FirstOrDefault();
            return Json(site, JsonRequestBehavior.AllowGet);
        }
        [ActionName("Details")]
        public ActionResult Details_NonAjax(int id)
        {
            var site = _sites.Where(s => s.Id == id).FirstOrDefault();
            return View(site);
        }

non-Ajax 버전의 view도 추가한다. 

Details.cshtml

@model AjaxExamples.Models.Site
<h2>Site Details: @Model.Name</h2>
<p class="site">
 <img src="@Model.PictureUrl" 
 alt="@Model.Name" /> 
 <span class="site-explain">@Model.Explanation</span> 
</p>
<br style="clear:both" />
@Html.ActionLink("Back to sites", "index")
728x90
반응형

댓글