AngularJS ile Dinamik Form Uygulaması

Herkese yeniden merhaba, yakın zamanda aklıma takılan bir şey için araştırmalar yapıyordum. Amacım web siteleri üzerinde dinamik ve kolay yönetilebilir formlar oluşturmaktı. Dinamik kelimesini açarsam; Önyüz ile html attribute’leriyle (name, required vs.) uğraşmadan, her proje içine kolayca entegre edilecek bir form oluşturma eklentisi. Buraya kadar olan özellikler bu işi standart yoldan değil de bir eklenti ile yapmanız için yeterli olmayabilir benim için de öyleydi fakat formlar içerisine Repeater benzeri kontroller eklemek isterseniz işler değişiyor.

Bir örnek üzerinden gidelim; Admin panelinizde bir sipariş formunuz var ve içerisinde no, tarih, müşteri adı soyadı gibi tekil alanların yanında ürün ekleme alanınız var. Yani “Ekle” butonuyla forma selectbox, textbox gibi kontrollerden oluşan bir Item ile ürün ekleyeceksiniz. Göründüğü üzere yapmak istediğimiz şey standart bir Html Form olmaktan çıkıp spesifik işlemler yapmamız gereken bir şeye dönüşüyor ve bu işlemleri her proje için ayrı ayrı yapmak zorunda kalabiliyoruz. Bu sebeple AngularJS ile basit bir uygulama geliştirdim. İhtiyaçlara göre daha da geliştirilebilir. Uygulamanın back-end tarafı Asp.Net MVC ile yazıldı, front-end tarafında AngularJS ve JQuery kullanıldı. Çalıştığınız platform çok önemli değil PHP ile de çalışabilirsiniz önemli olan formunuzun angularjs uyumlu Html çıktısını üretebiliyor olmanız.

Şimdi projenize eklemek istediğiniz formun aşağıdaki gibi olduğunu düşünelim bu formu kullandığınız platformda oluşturup AngularJS’ye göndermelisiniz.

    <div ng-app="app">
      <form method="post" class="df-form">
        <div ng-controller="mycontroller">

      <br />
      <input type="text" name="isim" value="{{ data.fields.isim }}" class="form-dfcontrol"/>
      <br />
      <br />
      <input type="text" name="soyisim" value="{{ data.fields.soyisim }}" class="form-dfcontrol"/>
      <br />

          <h3>Repeater 1</h3>
          <button type="button" ng-click="AddFunction('repeater1')">+ Ekle</button>
          <br />
          <br />
           <div id="repeater1" class="repeater">
            <div class="repeater-item" ng-repeat="item in data.repeater1">
              <input type="text" name="ders" value="{{ item.ders }}"/>
              <input type="text" name="bolum" value="{{ item.bolum }}"/>
              <input type="button" value="Kaldır" ng-click="RemoveFunction($index, 'repeater1')"/>
            </div>
           </div>
          <br />

        <button type="submit">Gönder</button>

      <pre></pre>
  </div>
      </form>
    </div>

    <script>
        var app = angular.module('app', []);

        app.controller('mycontroller', ['$scope', function ($scope) {
            // data back-end tarafından veritabanından getirilen JSON datası olacak
            $scope.data = {
                fields: { isim: "tayyip", soyisim: "yetiş" },
                repeater1: []
            };;
            $scope.AddFunction = function (repeaterName) {
                $scope.data[repeaterName].push({});
            };
            $scope.RemoveFunction = function (index, repeaterName) {
                $scope.data[repeaterName].splice(index, 1);
            };
        }]);
    </script>

Bu uygulamada veri akışı JSON ile sağlanıyor. Back-end tarafından gönderilen JSON verisi angularjs tarafından html form’a dönüştürülüyor form post edildiği zaman bu sefer JQuery tarafından bir JSON verisi üretilip Back-end tarafına gönderiliyor ve veritabanına kaydediliyor. Şimdi JQuery tarafından JSON Serialize işlemini yapalım

$("body").on("submit", ".df-form", function () {
    var formId = "#" + $(this).attr("id");
    var jdata = {
        fields: $(formId + " .form-dfcontrol > input, " + formId + " .form-dfcontrol > select").serializeObject()
    };

    $.each($(".repeater"), function () {
        var id = $(this).attr("id");
        var items = [];

        $.each($(this).find(".repeater-item"), function () {
            items.push($(this).find("input, select, textarea").serializeObject());
        });

        var newitem = { [id]:  items};
        $.extend(jdata, newitem);
    })

    var json = JSON.stringify(jdata, null, 2);
    $("pre").show().html((json));

    $.post(ajaxurl, "data=" + JSON.stringify(jdata), function(){
        location.reload();
    });
    return false;
});

Bu işlemle bir json verisi üretmiş olduk örnek bir json verisi aşağıdaki gibidir. Repeater kontrolleri için node’lar otomatik eklenecektir ve her bir item bir array olarak bağlanacaktır.

{
  "fields": {
    "isim": "tayyip",
    "soyisim": "yetiş",
    "telefon": "06345345",
    "isremember": "on",
    "diller": "1"
  },
  "RepeaterDersler": [
    {
      "dersadi": "matematik",
      "dersnot": "3"
    },
    {
      "dersadi": "fizik",
      "dersnot": "3"
    },
    {
      "dersadi": "kimta",
      "dersnot": "5"
    }
  ]
}

Buraya kadar olan işlemler platformdan bağımsız işlemlerdi yani Asp.net, PHP, Java, Django vs. herhangi bir web teknolojisi ile üretmiş olduğunuz html çıktısı ile rahatça çalışabilirsiniz. yukarıda dediğim gibi bu örnekte Asp.Net MVC C# kullandım örnek olması açısından onun da kodlarını aşağıya ekledim. inceleyebilirsiniz.

FormBuilder.cs

    public class FormBuilder
    {
        public string Name { get; set; }
        public string ControlContainer { get; set; }
        public string Data { get; set; }
        public List<IControl> Controls { get; set; }
        public FormBuilder()
        {
            Controls = new List<IControl>();
        }
        public HtmlString Render()
        {
            string controlcontainer = "<div class='form-dfcontrol'>{0}</div>";
            string result = string.Join("", Controls.Select(n => string.Format(ControlContainer, string.Format(controlcontainer, n.Render()))));

            string template = @"<div ng-app=""app"">
                                  <div ng-controller=""mycontroller"">
                                   <form id=""" + Name + @""" class=""df-form"" method='post'>
                                     [body] 
                                     <br/><br/>
                                     <button type='submit'>Gönder</button> 
                                   </form>
                                  </div>
                                </div>";

            template = template.Replace("[body]", result);
            return new HtmlString(template);
        }
        public HtmlString ScriptRender()
        {
            Data = Convert.ToString(HttpContext.Current.Session["form"]);
            string repeater_names = string.Join(",", Controls.Where(n => n.GetType() == typeof(Repeater)).Select(n => n.Name + ": []"));
            if (string.IsNullOrEmpty(Data))
                Data = "{ fields: {}, " + repeater_names + " };";

            string result = @"<script>
        var app = angular.module('app', []);
        var ajaxurl = '/home/save';
        var customdata = " + Data + @"

        app.controller('mycontroller', ['$scope', function ($scope) {
            $scope.data = customdata;
            $scope.AddFunction = function (repeaterName) {
                $scope.data[repeaterName].push({});
            };
            $scope.RemoveFunction = function (index, repeaterName) {
                $scope.data[repeaterName].splice(index, 1);
            };
        }]);

    </script>";

            return new HtmlString(result);
        }
    }

IControl.cs

    public interface IControl
    {
        string Name { get; set; }
        string RepeaterName { get; set; }
        HtmlString Render();
    }

TextBox.cs

    public class TextBox : IControl
    {
        public string Name { get; set; }
        public string RepeaterName { get; set; }
        public HtmlString Render()
        {
            string nodename = "";
            if (RepeaterName != null) nodename = "item";
            else nodename = "data.fields";
            return new HtmlString("<input type='text' name='" + Name + "' placeholder='" + Name + "' value='{{ " + nodename + "." + Name + " }}'/>");
        }
    }

Repeater.cs

public class Repeater : IControl
    {
        public string Name { get; set; }
        public string RepeaterName { get; set; }
        public List<IControl> Controls { get; set; }
        public Repeater()
        {
            Controls = new List<IControl>();
        }
        public HtmlString Render()
        {
            string result = string.Join("", Controls.Select(n =>
            {
                n.RepeaterName = Name;
                return n.Render();
            }));

            string template = @"<h3>" + Name + @"</h3>
          <button type=""button"" ng-click=""AddFunction('" + Name + @"')"">+ Ekle</button>
           <div id=""" + Name + @""" class=""repeater"">
            <div class=""repeater-item"" ng-repeat=""item in data." + Name + @""">
              [body]
              <input type=""button"" value=""Kaldır"" ng-click=""RemoveFunction($index, '" + Name + @"')""/>
            </div>
           </div>";
            template = template.Replace("[body]", result);
            return new HtmlString(template);
        }
    }

Son olarak da Html formumuzu inşa etme işlemini yapalım.

FormBuilder frmbuilder = new FormBuilder();
    frmbuilder.Name = "orderform";
    frmbuilder.ControlContainer = "<div class='container'>{0}</div>";
    frmbuilder.Controls = new List{
        new TextBox { Name = "isim" },
        new TextBox { Name = "soyisim" },
        new TextBox { Name = "telefon" },
        new CheckBox { Name = "isremember", Text = "Şifremi Hatırla" },
        new SelectBox { Name = "diller", Items = new List<SelectListItem>{ 
            new SelectListItem{ Value = "0", Text = "İngilizce" },
            new SelectListItem{ Value = "1", Text = "Almanca" },
            new SelectListItem{ Value = "2", Text = "Rusça" } 
        }},
        new Repeater{ Name = "RepeaterDersler", Controls = new List<IControl>{
            new TextBox{ Name = "dersadi" },
            new TextBox{ Name = "dersnot" }
        }}
    };

Şimdi formumuzu View içerisinde Render edelim. İki ayrı render fonksiyonumuz var biri Html için render diğeri angularjs için ScriptRender.

@using WebApplication1
<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <script src="~/content/angular.js"></script>
    <script src="~/Content/jquery-1.11.3.min.js"></script>
    <script src="/content/custom.js"></script>

    @frmbuilder.Render()
    @frmbuilder.ScriptRender()


</body>
</html>

Bu işlemleri bir kere yaptıktan sonra FormBuilder sınıfını kullanarak tekrar tekrar formlar oluşturabilirsiniz. Elbette bu örnekte sınırlı sayıda kontrol kullanıldı örneği geliştirerek daha kullanılabilir hale getirebilirsiniz. Burada formun post işlemlerini gerek duymadığım için göstermedim örnekteki ajaxurl değişkenini formun post edileceği adres olarak değiştirdiğinizde kalan kısım normal işlemlerden farklı değil.

Makalenin sonuna geldik. Umarım faydalı olmuştur.

Mustafa Tayyip YETİŞ
Yazılım Geliştirme Uzmanı

4.7 Ort. (91% puan) - 3 oy

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir