How can i set rowspan value dynamically to work with angularjs on date filed ?

7,234 views
Skip to first unread message

Ahmed Amen

unread,
Jun 5, 2015, 7:18:54 PM6/5/15
to ang...@googlegroups.com
Hi

i try here to view customers data on my view throw API i have "ID, Name, MyDate", but i need it viewed on table grouped by same month like:

but i don't know how to set it works i try many times as:

rowspan="{{cus.MyDate.length+1}}"

but nothing - my html code is:

<div ng-app="jqanim" ng-controller="ttController">
    <table class="table table-bordered">
        <tr>
            <th>CustomerID</th>
            <th>CustomerName</th>
            <th>CustomerMonth</th>
        </tr>
        <tr ng:repeat="cust in customers">
            <td>
                {{cust.ID}}
            </td>
            <td>{{cust.Name}}</td>
            <td rowspan="{{cus.MyDate.length+1}}"> @* here i need to use rowspan on month value on MyDate ?? *@
                {{cust.MyDate | date: "MMMM"}}
            </td>
        </tr>
    </table>
</div>

js code:

(function () {
    var app = angular.module('jqanim', []);

    app.controller('ttController', function ($scope, $http) {

        $http.get("/api/Test").success(function (data) {
            $scope.customers = data;
        });

    });
})();

api controller code:

        private static List<Test> listOfOrders;


        public TestController()
        {
            if (listOfOrders == null)
            {
                listOfOrders = new List<Test>
                {
                    new  Test { ID = 1, Name = "Customer1" , MyDate = new DateTime(2015,01,01)},

                    new  Test { ID = 2, Name = "Customer2" , MyDate = new DateTime(2015,01,01)},

                    new  Test { ID = 3, Name = "Customer3" , MyDate = new DateTime(2015,02,05)},

                    new  Test { ID = 4, Name = "Customer4" , MyDate = new DateTime(2015,01,05)},

                    new  Test { ID = 5, Name = "Customer5" , MyDate = new DateTime(2015,02,05)},

                    new  Test { ID = 6, Name = "Customer6" , MyDate = new DateTime(2015,02,05)},

                    new  Test { ID = 7, Name = "Customer7" , MyDate = new DateTime(2015,02,05)},

                    new  Test { ID = 8, Name = "Customer8" , MyDate = new DateTime(2015,03,05)},

                    new  Test { ID = 9, Name = "Customer9" , MyDate = new DateTime(2015,04,05)}
                };
            }
        }

        public IEnumerable<Test> Get()
        {
            return listOfOrders.OrderBy(i => i.MyDate);
        }

so please how can i make it works ?

Sander Elias

unread,
Jun 6, 2015, 1:31:47 AM6/6/15
to ang...@googlegroups.com
Hi Ahmed,

Don't try to solve this in the template. While it is possible, you will offload a lot of complexity into your template that does not belong there.
do something like this in your controller. (For your reference, if you have an $http call in your controller, that is a sure sign you need a service/factory, as a rule of thumb, don't do $http in a controller)

$http.get("/api/Test").success(function (data) {
   
var dataByMonth = [];
    data
.forEach(itemToMonth);
    $scope
.customers = dataByMonth;

   
function itemToMonth(item) {
       
var month = item.MyDate.getMonth;
        dataByMonth
[month] = dataByMonth[month] || [];
        dataByMonth
[month].push(item);
   
}
});

Then you can simply nest the repeat, and be done with it.
When you have difficulties doing this, set up a plunk, and I will take a look for you.

Regards
Sander

Ahmed Amen

unread,
Jun 6, 2015, 3:38:50 AM6/6/15
to ang...@googlegroups.com
Thanks a lot Sander

so sorry for not understanding you as it should be - i'm new with angular - i have add sample project here: http://plnkr.co/edit/JERX3iqg1mF7M0TBpLH2?p=preview

hope you take a look and help me (i have create an array has some data but i can't view it with angular )

Robert STAICU

unread,
Jun 6, 2015, 4:53:35 AM6/6/15
to ang...@googlegroups.com
I have a little plunker on what i think Sander want you to do with data from $http. 


Now i need to leave so i can't help you any further...

If i'm corect about what i think Sander want to mean, you now should just template...

When i'm gonna come back i'll assist you further.

Good bye

Ahmed Amen

unread,
Jun 6, 2015, 6:28:15 AM6/6/15
to ang...@googlegroups.com
Thanks a lot - i still waiting Sander to see what we can do ....

Sander Elias

unread,
Jun 6, 2015, 8:03:46 AM6/6/15
to ang...@googlegroups.com
Hi Ahmed,

here you go: link to plunk
If you have question on why and how, feel free to ask :)

Regards
Sander

Ahmed Amen

unread,
Jun 6, 2015, 8:32:34 AM6/6/15
to ang...@googlegroups.com
Thanks so much it's works great now :)

but please why and how :) ??!!

Sander Elias

unread,
Jun 6, 2015, 9:36:51 AM6/6/15
to ang...@googlegroups.com
Hi Ahmed,

Please be more specific, on what you want to know. I don't have time to explain everything I did in there. I assume you can get your grasp on most of it by yourself. Play around with it a bit. :)

Ahmed Amen

unread,
Jun 6, 2015, 11:32:43 AM6/6/15
to ang...@googlegroups.com
Thanks for inform me

please can you tell me what is the mean of that:

ng-if="$first"

as i understand in js code you loop throw MyDate filed to get it but i can't understand this part:

            dataByMonth[month] = dataByMonth[month] || [];
            dataByMonth[month].push(item);

thanks a lot and have a great day ..... :)



Robert STAICU

unread,
Jun 6, 2015, 12:38:34 PM6/6/15
to ang...@googlegroups.com
ng-if="$first" means that only if is the first object from ng-repeat="cust in month" it will show month. It's angular staff. He does that to ensure that only first row will show month. You can either try $last in place of $first and Jan Febr etc will show last from their group. Also if you remove that ng-if all records will have months.

dataByMonth[month] = dataByMonth[month] || [];
dataByMonth[month].push(item);
Inhere he just initialize dataByMonth to be an empty array if doesn't exist.

dataByMonth[month] = (dataByMonth) ? dataByMonth[month] : [];

dataByMonth[month].push(item); // He just push records into months. In january he push 3 records {id: 1, name: 'Senior'}, {id:2, 'Senior'}, {id:3, 'Adult'}

And in html he do 2 repeat. 
1. For month to get all months with their records.
2. Get all records from current month.

Hope this helps. If any other questions i will help you. Sander i hope you'll not get angry.

A nice day to both of you

Sander Elias

unread,
Jun 6, 2015, 1:45:38 PM6/6/15
to ang...@googlegroups.com
Hi Robert,

Your explanation is sound. But i don't understand one thing, why do you think I might get angry?

Regards
Sander

Ahmed Amen

unread,
Jun 6, 2015, 2:04:59 PM6/6/15
to ang...@googlegroups.com
Thanks Both of you ....

Robert STAICU

unread,
Jun 6, 2015, 2:06:32 PM6/6/15
to ang...@googlegroups.com
Cause you helped him:D

Also this should be an update to your strategy Ahmed. If you have 3 records from Jan 2015 and 2 from Jan 2016 you will have 5 records from Jan. I think you'll need to distinguise year also.
http://plnkr.co/edit/bObFZarT4eKqHxJrusAq?p=preview

I only added one filter that will order your records. You can change orderObjectBy: 'MyDate': true from true to false to order them. Have a nice day


Ahmed Amen

unread,
Jun 6, 2015, 2:16:56 PM6/6/15
to ang...@googlegroups.com
Thanks a lot Robert for your important note

but please why you use filter here and sorry can you explain filter code please ?

i try it without filter and it works as i need:

                item.MyDate = new Date(item.MyDate)
                var month = item.MyDate.getMonth();
                var year = item.MyDate.getFullYear();
                dataByMonth[year + '-' + month] = dataByMonth[year + '-' + month] || [];
                dataByMonth[year + '-' + month].push(item);

may be because i have set order by in my C# controller as:

        public IEnumerable<Test> Get()
        {
            return listOfOrders.OrderBy(i => i.MyDate).ThenBy(i => i.ID);
        }


Message has been deleted

Robert STAICU

unread,
Jun 7, 2015, 4:43:57 AM6/7/15
to ang...@googlegroups.com
Ok so i made a filter cause you can include it in BaseModule. Think using 5 modules "MenuModule", "TableModule", "UserModule", "LoginModule", "ExampleModule".
You can use this filter in any of 5 modules i exampled to you. You can have a "BaseModule" that can include filter "orderObjectBy" and all of your modules are gonna inherit that. 

My filter basically  transform $scope.customersByMonth from object to array and then sort it. "function(items, filter, reverse)"  params are gonna be:
1. $scope.customersByMonth[month] = array of object of month                                        (params.items)
2. 'MyDate' = date of that month(a[0][filter] && b[0][filter]) //filter being "MyDate"                 (params.filter)
3. if reverse = false or true                                                                                                  (params.reverse)

You can console.log() them to see what is going on.

I know you can order your data from server, but in this case your data will be ordered by the front-end. You can do both of them or just one of them.

I suggest doing both of them if you want to make a directive with this and public it. If you use just for your own scope you can do the server stuff.

If any rows isn't clear just ask. I'll be glad to answer. 

Have a nice day

Ahmed Amen

unread,
Jun 10, 2015, 6:18:16 AM6/10/15
to ang...@googlegroups.com
Thanks Robert 

it works fine now but please i have an problem with other Angular Project and hope that you can help me with that, i have create an invoice with angular and pass data throw API Controller - i store data in list to let me save it on database when i need, but when i open new invoice from my PC and add item's on it before i save it to database i open another invoice from other PC and when it's open i got the item's from invoice in my pc on it too - i don't know how to set it to open with empty array in each PC to be works as it should be ?

Angular Code:

(function () {
    var app = angular.module('jqanim', []);

    app.controller('InvoiceController', function ($scope, $http) {
        $scope.tax = 0.00;
        $scope.PercentageDiscount = 0.00;
        $scope.MoneyDiscount = 0.00;
        $scope.Total = 0.00;

        $scope.save = function () {
            $http.post("/api/customerService/Post", $scope.item).success(function (data) {
                $scope.orders.push(data);

                // Clear AutoComplete Text
                $("#Products").data("kendoAutoComplete").value("");

                if (window.event.keyCode == 13) {
                    $("#ProductID").focus();
                }

                $('#ProductID').prop('readonly', false);
            });
        };

        $scope.saveInvoice = function () {
            $http.post("/api/customerService/SaveInvoice", $scope.invoice)
                .success(function (data) {
                    // Clear AutoComplete Text
                    $("#Customers").data("kendoAutoComplete").value("");

                    $scope.invoice = [];
                    $scope.orders = [];

                    $scope.tax = 0.00;
                    $scope.PercentageDiscount = 0.00;
                    $scope.MoneyDiscount = 0.00;

                    alertify.set('notifier', 'position', 'top-left');
                    alertify.success('تم حفظ الفاتورة', 3);
                })
                .error(function (data) {
                    alertify.set('notifier', 'position', 'top-right');
                    alertify.error('يوجد خطأ', 2);
                    return;
                });
        };

        $scope.remove = function (id, index) {
            $http.delete("/api/customerService?id=" + id).success(function (data) {
                $scope.orders.splice(index, 1);
            });
        };

        $http.get("/api/CustomerService/getDetails").success(function (data) {
            $scope.orders = data;
        });

        $scope.keypress = function (id) {
            if (window.event.keyCode == 13) {

                $http.get("/api/CustomerService/GetProduct?id=" + id).success(function (data) {

                    angular.forEach(data, function (item) {
                        $("#Products").val(item.ProductName).trigger('change');

                        $("#SellPrice").val(item.sell_price).trigger('change');

                        $("#Stock").val(item.stock).trigger('change');

                        $("#Quantity").focus();
                    });

                });
            }
        };
    });
})();


my view code:

    <form method="post" ng-submit="save(); item={}">
        <table class="table table-striped table-bordered" dir="rtl">
            <tr>
                <th class="text-center">كود الصنف</th>
                <th class="text-center">إسم الصنف</th>
                <th class="text-center">الكمية</th>
                <th class="text-center">السعر</th>
                <th class="text-center">الرصيد</th>
                <th class="text-center">الإجمالى</th>
                <th></th>
            </tr>
            <tr>
                <td style="vertical-align:middle;">
                    @*<input type="number" step="any" class="k-textbox trv-parameter-editor-number" id="ProductID" ng-model="item.ProductID" ng-keypress="keypress(item.ProductID,$index)">*@
                    <input id="ProductID" type="text" placeholder="حدد الكود" class="text-center form-control" style="width:100px; font-weight:bold" />
                </td>
                <td align="center" style="vertical-align:middle;">
                    <div>
                        @(Html.Kendo()
                              .AutoComplete()
                              .Name("Products")
                              .DataTextField("ProductName")
                              .Events(events => events.Select("ProductSelect"))
                              .MinLength(1)
                              .HtmlAttributes(new { style = "width:450px; font-weight:bold", ng_model = "item.ProductName" })
                              .Placeholder("إكتب جزء من إسم الصنف")
                              .Filter("contains")
                              .Delay(1)
                              .DataSource(source =>
                              {
                                  source.Custom()
                                        .ServerFiltering(true)
                                        .ServerPaging(true)
                                        .Type("aspnetmvc-ajax") //Set this type if you want to use DataSourceRequest and ToDataSourceResult instances
                                        .Transport(transport =>
                                        {
                                            transport.Read("Virtualization_Read", "Home");
                                        })
                                        .Schema(schema =>
                                        {
                                            schema.Data("Data") //define the [data](http://docs.telerik.com/kendo-ui/api/javascript/data/datasource#configuration-schema.data) option
                                                  .Total("Total"); //define the [total](http://docs.telerik.com/kendo-ui/api/javascript/data/datasource#configuration-schema.total) option
                                        });
                              })
                              .Virtual(v => v.ItemHeight(26).ValueMapper("valueMapper"))
                        )

                    </div>
                </td>
                <td align="center" style="vertical-align:middle;">
                    <input ng-readonly="item.ProductID == '' || item.ProductID == null" id="Quantity" name="Quantity" class="text-center form-control" ng-model="item.Quantity" value="1" size="4" ng:required ng:validate="integer" placeholder="حدد الكمية" style="width:100px; font-weight:bold">
                </td>
                <td align="center" style="vertical-align:middle;">
                    <input ng-readonly="true" id="SellPrice" class="text-center form-control" ng-model="item.SellPrice" value="0.00" ng:required ng:validate="number" size="6" style="width:100px; font-weight:bold">
                </td>
                <td align="center" style="vertical-align:middle;">
                    <input ng-readonly="true" id="Stock" class="text-center form-control" ng-model="item.Stock" value="0.00" ng:required ng:validate="number" size="6" style="width:100px; font-weight:bold">
                </td>

                <td class="text-center" style="vertical-align:middle; width:100px; font-weight:bold;">
                    {{item.SellPrice * item.Quantity | currency}}
                    @Html.TextBox("Total_Item", null, new { ng_model = "item.Total_Item", @style = "display:none;" })
                </td>

                <td align="center" style="vertical-align:middle;">
                    <input ng-disabled="item.Quantity > item.Stock || item.Quantity == 0 || item.Quantity == null || item.SellPrice == null" type="submit" name="submitbtn" value="إضافة" class="btn btn-primary" style="font-weight:bold;" />
                </td>
            </tr>
            <tr>
                <td colspan="7" class="text-center" style="color:red; font-weight:bold;">
                    <div ng-show="item.Quantity > item.Stock">رصيد الصنف لا يكفى</div>
                    <div ng-show="item.Quantity == 0">غير مسموح بكمية أقل من 1</div>
                </td>
            </tr>
        </table>
    </form>
    <br />
    @* Details *@
    <table class="table table-striped table-bordered" align="center" dir="rtl" ng-show="(orders).length">
        <tr>
            <th class="text-center" style="width:100px;">كود الصنف</th>
            <th class="text-center">إسم الصنف</th>
            <th class="text-center" style="width:150px;">الكمية</th>
            <th class="text-center" style="width:150px;">السعر</th>
            <th class="text-center" style="width:150px;">الإجمالى</th>
            <th style="width:150px;"></th>
        </tr>
        <tr ng-repeat="ord in orders">
            <td align="center" style="vertical-align:middle; font-weight:bold;">
                {{ord.ProductID}}
            </td>
            <td align="center" style="vertical-align:middle; font-weight:bold;">
                {{ord.ProductName}}
            </td>
            <td align="center" style="vertical-align:middle; font-weight:bold;">
                {{ ord.Quantity}}
            </td>
            <td align="center" style="vertical-align:middle; font-weight:bold;">
                {{ord.SellPrice}}
            </td>
            <td class="text-center" style="vertical-align:middle; font-weight:bold;">
                {{ord.SellPrice * ord.Quantity | currency}}
            </td>
            <td align="center" style="vertical-align:middle;">
                <input type="submit" name="submitbtn" value="x" class="btn btn-danger" ng-click="remove(ord.ProductID,$index)" style="font-weight:bold;" />
            </td>
        </tr>

    </table>

API Controller Code:

        readonly Model1 db = new Model1();

        private static List<Order_Details_VM> listOfOrders;

        private static List<Product> listOfProducts;

        int statusCode = 0;

        public CustomerServiceController()
        {
            listOfProducts = null;

            if (listOfProducts == null)
            {
                listOfProducts = new List<Product>();

                foreach (var pro in db.Products)
                {
                    Product p = new Product();

                    p.ProductID = pro.ProductID;
                    p.ProductName = pro.ProductName;
                    p.sell_price = pro.sell_price;
                    p.stock = pro.stock;
                   
                    listOfProducts.Add(p);
                }
            }

            if (listOfOrders == null)
            {
                listOfOrders = new List<Order_Details_VM>();
            }
        }

        public IHttpActionResult GetDetails()
        {
            return Ok(listOfOrders);
        }

        public IHttpActionResult GetProduct(int id)
        {
            var selectedProduct = listOfProducts.Where(i => i.ProductID == id).ToList();
            return Ok(selectedProduct);
        }

        public IHttpActionResult Post(Order_Details_VM newOrderDetails)
        {
            if (newOrderDetails != null)
            {
                listOfOrders.Add(newOrderDetails);

                return Ok(newOrderDetails);
            }
            return null;
        }

        public IHttpActionResult SaveInvoice(Order_VM newOrderMaster)
        {
            // save on header table to get invoice id
            if (newOrderMaster != null)
            {
                var order = new Order();

                order.CustomerID = newOrderMaster.CustomerID;
                order.OrderDate = newOrderMaster.OrderDate;
                order.Freight = (decimal)newOrderMaster.Total;
                //order.type = "بيع";
                db.Orders.Add(order);
                db.SaveChanges();

                newOrderMaster.OrderID = order.OrderID;
            }

            // loop throw items list to check Stock & add to database with invoice no
            foreach (var item in listOfOrders)
            {
                item.OrderID = newOrderMaster.OrderID;

                // ckeck Stock in online database not in the list
                var model = db.Products.Find(item.ProductID);

                if (model.stock >= item.Quantity)
                {
                    model.stock = model.stock - item.Quantity;
                    db.Entry(model).State = EntityState.Modified;
                    statusCode = 200;
                }
                else
                {
                    statusCode = 404;
                }

                if (statusCode == 200)
                {
                    // save to DB
                    var orderdetails = new Order_Detail();

                    orderdetails.OrderID = item.OrderID;
                    orderdetails.ProductID = item.ProductID;
                    orderdetails.UnitPrice = (decimal)item.SellPrice;
                    orderdetails.Quantity = item.Quantity;
                    orderdetails.Quantity_buy = item.Quantity;
                    orderdetails.Total = item.Total_Item;

                    db.Order_Details.Add(orderdetails);
                    db.SaveChanges();
                }
                else
                {
                    var x = item.ProductName;
                }
            }

            listOfOrders.Clear();

            return Ok();
        }

        public Order_Details_VM Delete(int id)
        {
            if (id > 0)
            {
                // here by productis
                var selectedCustomer = listOfOrders.Find(p => p.ProductID == id);
                if (selectedCustomer != null)
                {
                    listOfOrders.Remove(selectedCustomer);
                    return selectedCustomer;
                }

            } return new Order_Details_VM();
        }


sorry for long code but hope that you can help me please ?

Thanks a lot ....

Robert STAICU

unread,
Jun 10, 2015, 8:22:46 AM6/10/15
to ang...@googlegroups.com
Hello again. I think you should've done another post for this.

I looked over your code. I pasted it into my notepad++ and deleted saveInvoice cause is never called, remove() cause i don't think it's relevant, keypress never called(I think cause it was commented that input).

What i really think is wrong is your backend. I really haven't found any mistake in angular stuff. So let's clear something. 

You have a form that comunicate with server trough kendo(i don't know nothing about kendo just that "is two way data binding"). You create a new invoice that is stored live to your server and updated live too. And if you don't save it and go on another Pc you can see that invoice.
If that is the case i think you should rethink this.

If i press create i would create a new invoice with an id of 1(let's say one cause it doesn't exist for now). I just play with that.
If i press again create i would create a new invoice with an id of 2(cause 1 already exist) And i'll be sure that every time i create a new invoice on my backend i'll do it empty.

So your url will look like this(back-end and front-end) 'blablabla/invoice/1' and 'blablabla/invoice/2' Your server will look for 1 or 2 and get that specific data. This is called restfull so i think that's all you wanna do in this case.

This is what i understood from your code. If i'm mistake corect me.

Have a nice day...and btw when you will get details your url will be "/api/CustomerService/invoice/1/getDetails" and "/api/CustomerService/invoice/2/getDetails"

Ahmed Amen

unread,
Jun 10, 2015, 8:49:57 AM6/10/15
to ang...@googlegroups.com
Thanks Robert

sorry for bad understanding you i have create new post for that on : https://groups.google.com/forum/#!topic/angular/M_DtyUKAWJY

i have added there full codes hope you reply there with more details or example to let me understand it 

Note: i try other solution by set listOfOrders = null; in my api Controller Constractor and pass listOfOrders in other list to loop throw it when need to save data in database (that works)

but i need to know other way to learn how to do it with angular (set this section empty when i open new invoice in any pc ) i think problem is here

        <tr ng-repeat="ord in orders">
            <td align="center" style="vertical-align:middle; font-weight:bold;">
                {{ord.ProductID}}
            </td>
            <td align="center" style="vertical-align:middle; font-weight:bold;">
                {{ord.ProductName}}
            </td>
            <td align="center" style="vertical-align:middle; font-weight:bold;">
                {{ ord.Quantity}}
            </td>
            <td align="center" style="vertical-align:middle; font-weight:bold;">
                {{ord.SellPrice}}
            </td>
            <td class="text-center" style="vertical-align:middle; font-weight:bold;">
                {{ord.SellPrice * ord.Quantity | currency}}
            </td>
            <td align="center" style="vertical-align:middle;">
                <input type="submit" name="submitbtn" value="x" class="btn btn-danger" ng-click="remove(ord.ProductID,$index)" style="font-weight:bold;" />
            </td>
        </tr>


Thanks a lot .... 
Reply all
Reply to author
Forward
0 new messages