I am using angularJS ontop of my application.
I have a basic example of a controller:
function OrderListCtrl($scope, $http) {
$http.get('/static/angular/app/phones/van1.json').success(function(data) {
$scope.van1 = data;
});
$http.get('/static/angular/app/phones/van2.json').success(function(data) {
$scope.van2 = data;
});
}
And a sample JSON entry
{
"id": "3",
"custName": "Mrs Smith",
"accountNumber": "416",
"orderNumber": "12348",
"orderWeight": "120.20"
},
My html looks like this:
<div id=1>
<h1>Van 1 - Total Weight = XXX </h1>
<ul class="sortdrag">
<li ng-repeat="van1 in van1" id="[[ van1.id ]]">
[[van1.custName]] [[van1.accountNumber]] [[van1.orderWeight]]
</li>
</ul>
</div>
Now, I want to get the total weight for every li item in the ul.
This WOULD be easy if the lists where static, however the lists are using jQuery-ui and I have multiple lists where the li items are dragged and dropped between each list. My question is, how can I have the XXX dynamically update to the value of all weights in each li in the ul, or more to the question can this even be done?
I dont really want to use an onDrop event as this will not work on the pre-populated lists, so ideally I would like to use code that takes its values from all van1.orderWeight values in the ul.
Any suggestions on the best way to approach this would be very much appreciated! And before anyone asks im using [[ and ]] as opposed to {{ and }} because I am using jinja2 templates.
UPDATE:
Ok to after reading the answer below have amended the original controller to:
function OrderListCtrl($scope, $http) {
$http.get('/static/angular/app/phones/van1.json').success(function(data) {
$scope.van1 = data;
// This part is easy, calcuate the sum of all weights from the JSON data
$scope.sumV1 = _.reduce(_.pluck($scope.van1, 'orderWeight'), function (m, w) {return m + w}, 0);
});
$scope.getVan1Weight = function(){
// here I try and write a function to calculate the dynamic weight
// of a van, which will update when items get dropped in/out of the ul
_.reduce(_.pluck($scope.van1, 'orderWeight'), function (m, w) {return m + w}, 0);
}
And my template
<div id="app" ng-controller="OrderListCtrl">
<div id=1>
<h1>Van 1 - Total Weight = [[getVan1Weight()]]
Starting Weight - [[sumV1]]</h1>
<ul class="sortdrag">
<li ng-repeat="van1 in van1" id="[[ van1.id ]]">
[[van1.custName]] [[van1.accountNumber]] [[van1.orderWeight]]
</li>
</ul>
</div>
Now im using the underscorejs libary to help perform the calculations, but I can only seem to get this to work using the initial data, and not to update when new orders are dragged in from another ul
That is pretty each to achieve in Angular. You have to write a function in your controller that does the calculation for you and interpolate that function in your view. Something like
<div id=1>
<h1>Van 1 - Total Weight = [[getVanWeight()]] </h1>
<ul class="sortdrag">
<li ng-repeat="van1 in vans" id="[[ van1.id ]]">
[[van1.custName]] [[van1.accountNumber]] [[van1.orderWeight]]
</li>
</ul>
</div>
Inside your controller you do :
$scope.getVanWeight = function(){
// write your logic for calculating van weight here.
}
Thanks to #ganaraj I used the guide found at
http://www.smartjava.org/content/drag-and-drop-angularjs-using-jquery-ui
I also replaced my underscore code with pure angularjs code
$scope.getVan1Weight = function(){
var sum = 0;
for( t=0; t < $scope.van1.length; t++) { sum += $scope.van1[t].orderWeight }
return sum.toFixed(2);;
}
At the beginning of my controller I also defined an empty array (to get overwritten later)
// Define an empty array
$scope.van1 = [];
As this stops any errors producing as the $http get takes a while to load and when you load a browser window the .length returns an error, so if you define an empty array it stops any errors. I could have tested to see if an array was valid and put some if clauses in, but my angularjs knowledge is very limited so this solution worked best for me.
Here's something very similar to your requirement with a solution that works nicely...
Markup...
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Pillar Name</th>
<th>Weight</th>
<th>Metrics</th>
<th> Actions</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="p in l.pillars">
<td>{{p.name}}</td>
<td>{{p.weight}}</td>
<td>{{p.metrics.length}}</td>
<td>
<a href="" data-ng-click="vm.editPillar(p)">
<i class="fa fa-pencil blue"></i>
</a>
<a href="" data-ng-click="vm.deletePillar(p, l)">
<i class="fa fa-times-circle red"></i>
</a>
</td>
</tr>
<tr>
<td><strong>Totals</strong></td>
<td><strong>{{l.pillars.summedWeight}}</strong></td>
<td></td>
<td></td>
</tr>
</tbody>
js, which gets called when i get pillars from the server or local cache...
function getSummedPillarWeight(pillars) {
var summedWeight = 0;
pillars.forEach(function (pillar) {
summedWeight = summedWeight + pillar.weight;
});
return pillars.summedWeight = summedWeight;
}
Related
I try to make an if function in razor view, by comparing two data from viewbag in ASP NET MVC
#ver comes from ViewBag.Temp
<td class="text-center">
#foreach (Dummy ver in ViewBag.dummies)
{
if (#ver.Name == #item.Name)
{
<i class="fas fa-check" id="existed"></i>
}
}
</td>
I already have 2 same name data inside those 2 tables but it wont work
The Temp data
The Dummies Data
but when i call another data it works properly
Another data temp
Another data Dummies
It solved, there is some human error on the data that have white space on leading or trailing then im using trim() function on my view
look like this
<td class="text-center">
#foreach (Dummy ver in ViewBag.Dummies)
{
var tem = #item.Name.Trim();
if (#ver.Name.Trim() == tem)
{
<i class="fas fa-check" id="existed"></i>
}
}
</td>
I'm building an angularjs / firebase app unsing the angularfire bindings (v0.5.0).
I have a list of items, displayed in a table with ng-repeat on the <tr>, something like this:
<table>
<tbody>
<tr ng-repeat="(index, item) in items">
<td>
<input type="checkbox" ng-model="item.done">
</td>
<td>
<input type="text" ng-model="item.text" ng-focus="onItemFocus(index)" ng-blur="onItemBlur(index)">
</td>
<td>
<button type="button" ng-click="remove(index)">×</button>
</td>
</tr>
</tbody>
</table>
and there's an angularfire 3 way data binding on this list of items, something like:
$scope.ref = new Firebase('...');
$scope.remote = $firebase($scope.ref);
$scope.remote.$child("items").$bind($scope, "items");
This works all fine, but now I'm trying to add the possibility to reorder the items with drag and drop.
I managed to setup the drag and dropping UI with jquery-ui (essentially calling $("tbody").sortable()), but my problem is to bind it to the angular models. There's a number of questions regarding that (with great jsfiddles) but in my case the angularfire 3 way binding seems to be messing it up.
I think I need to use firebase priorities with angularfire's orderByPriority and maybe deal with it in one of the sortable callbacks but I'm having trouble figuring out exactly how I should do that... and can't find any sort of documentation about it.
Has anyone done something similar, and could you share some pointers on how to set this up?
I saw your post a long time ago while I was looking for the same solution. Here is something I put together:
function onDropComplete(dropIndex, item) {
if (!item.isNew){
var dragIndex = $scope.fireData.indexOf(item);
item.priority = dropIndex;
$scope.fireData.$save(dragIndex);
if (dragIndex > dropIndex){
while ($scope.fireData[dropIndex] && dropIndex !== dragIndex ){
$scope.fireData[dropIndex].priority = dropIndex+1;
$scope.fireData.$save(dropIndex);
dropIndex++;
}
} else if(dragIndex < dropIndex){
while ($scope.fireData[dropIndex] && dropIndex !== dragIndex ){
$scope.fireData[dropIndex].priority = dropIndex-1;
$scope.fireData.$save(dropIndex);
dropIndex--;
}
}
} else if (item.isNew){
item = angular.copy(item);
item.isNew = false;
item.priority = dropIndex;
$scope.fireData.$add(item);
while ($scope.fireData[dropIndex]){
$scope.fireData[dropIndex].priority = dropIndex+1;
$scope.fireData.$save(dropIndex);
dropIndex++;
}
}
}
Here, I take items already in the list and have the priority properties of items adjust on drop, depending if the item that was dragged was above or below the drop area. Also, if the drag item in new to the list, it will be added at index where dropped and all items below will be bumped up 1 priority. This is dependent on having your list sorted by var sync = $firebase(ref.orderByChild('priority'));, and you to be using ngDraggable.
Here is some HTML for an example:
<tr ng-repeat="obj in fireData" ng-drop="true" ng-drop-success="onDropComplete($index, $data,$event)">
<td draggable="true" ng-drag="true" ng-drag-data="obj">
<span class="glyphicon glyphicon-move"></span><div class="drag-name">{{obj.name}}</div>
</td>
<td>{{obj.name}}</td>
<td>{{obj.type}}</td>
<td><input type="checkbox" ng-change="saveChanges(obj)" ng-model="obj.completed"></td>
<td>
<div class="" ng-click="deleteItemFromList(obj)">
<span class="glyphicon glyphicon-remove"></span>
</div>
</td>
</tr>
I have a sortable list of items which are generated from an array property on a model. I want to use jquery UI sortable to reorder the items and save this order in my Action method.
Here is the code I am using to output the items:
#using(#Html.BeginForm("Save", "MyController", FormMethod.Post))
{
<input type="submit" value="Save"/>
#Html.HiddenFor(x=>x.Id)
<ul id="sortable2" class="connectedSortable">
#{
int count = 0;
}
#foreach(var item in Model.CollectionOfThings)
{
<li class="ui-state-default">
#Html.Hidden("Things[" + count + "].Id", item.Id)
#Html.Hidden("Things[" + count + "].Title", item.Title)
#item.Title
</li>
count++;
}
</ul>
}
When I receive the model in my action method the order is the same as when I started. I understand that this is because of the index but I can't figure out a way (without writing some custom javascript to update the name attribute of each hidden input) to have the items bound to the array in correct order.
Can anyone suggest a way this could be achieved? I can probably write a bit of Javascript to do it but I hoped I could do this purely with model binding.
I tried outputting the inputs like this:
#Html.Hidden("Things[].Id", item.Id)
However this just resulted in my collection being null.
I have discovered a blog post from Phil Haack which contained the answer.
By using arbitrary indexes in a seperate hidden input with the name of my collection + .Index, the model binder seems to handle it and maintain the order.
#using(#Html.BeginForm("Save", "MyController", FormMethod.Post))
{
<input type="submit" value="Save"/>
#Html.HiddenFor(x=>x.Id)
<ul id="sortable2" class="connectedSortable">
#{
int count = 0;
}
#foreach(var item in Model.CollectionOfThings)
{
<li class="ui-state-default">
#Html.Hidden("Things.Index", "item" + count)
#Html.Hidden("Things[item" + count + "].Id", item.Id)
#Html.Hidden("Things[item" + count + "].Title", item.Title)
#item.Title
</li>
count++;
}
</ul>
}
Easier solution for you can be to collect only Ids. I assume titles can be recreated on server side. In this case you can just define:
#Html.Hidden("ThingIds", item.Id)
and bind to
List<int> thingsIds
I have a table full of data, upon clicking on any of the record. A jquery ajax function called and get the detailed information for that record and display it in the div associated with the record.
Now i want to show it in a accordion.
Most of time a jquery accordion works like this
$(document).ready(function() {
$('#accordion').accordion();
});
But here i want that my div gets populated first with data then accordion method gets called. Because if accordion() called first then there is nothing for accordion to display as the request for data is still in processing.
My jquery ajax method is like this
$(function () {
$("span.Consignment").click(function () {
var position = 'div#' + this.innerHTML;
var url = "/Tracking/TrackingConsignment?consno=" + this.innerHTML;
$(position).load(url, function() {
$("a.Consignment").accordion();
return false;
});
});
});
This is my code
#foreach (var lst in item.Item2)
{
<a href="#" class="Consignment">
<table class="gridtable">
<a href="#">
<tr>
<td>
<span class="Consignment" href="#">#lst.ConsignmentNo</span>
</td>
<td>#lst.ConsignmentDate
</td>
</tr>
</a>
</table>
</a>
<div id="#lst.ConsignmentNo">
</div>
}
How should i make it work. First data then accordion.
Setup your accordion in the success of the .load.
.load(url,function(){
$("#accordion").accordion();
})
I'm iterating a List<T> in a razor foreach loop in my view which renders a partial. In the partial I'm rendering a single record for which I want to have 4 in a row in my view. I have a css class for the two end columns so need to determine in the partial whether the call is the 1st or the 4th record. What is the best way of identifying this in my partial to output the correct code?
This is my main page which contains the loop:
#foreach (var myItem in Model.Members){
//if i = 1
<div class="grid_20">
<!-- Start Row -->
//is there someway to get in for i = 1 to 4 and pass to partial?
#Html.Partial("nameOfPartial", Model)
//if i = 4 then output below and reset i to 1
<div class="clear"></div>
<!-- End Row -->
</div>
}
I figure I can create a int that I can update on each pass and render the text no problem here but it's passing the integer value into my partial I'm more concerned about. Unless there's a better way.
Here is my partial:
#{
switch()
case 1:
<text>
<div class="grid_4 alpha">
</text>
break;
case 4:
<text>
<div class="grid_4 omega">
</text>
break;
default:
<text>
<div class="grid_4">
</text>
break;
}
<img src="Content/960-grid/spacer.gif" style="width:130px; height:160px; background-color:#fff; border:10px solid #d3d3d3;" />
<p>#Model.Name<br/>
#Model.Job<br/>
#Model.Location</p>
</div>
Not sure if I'm having a blonde day today and this is frightfully easy but I just can't think of the best way to pass the int value in. Hope someone can help.
#{int i = 0;}
#foreach(var myItem in Model.Members)
{
<span>#i</span>
i++;
}
//this gets you both the item (myItem.value) and its index (myItem.i)
#foreach (var myItem in Model.Members.Select((value,i) => new {i, value}))
{
<li>The index is #myItem.i and a value is #myItem.value.Name</li>
}
More info on my blog post
http://jimfrenette.com/2012/11/razor-foreach-loop-with-index/
Or you could simply do this:
#foreach(var myItem in Model.Members)
{
<span>#Model.Members.IndexOf(myItem)</span>
}
Take a look at this solution using Linq. His example is similar in that he needed different markup for every 3rd item.
foreach( var myItem in Model.Members.Select(x,i) => new {Member = x, Index = i){
...
}
You could also use deconstruction and tuples and try something like this:
#foreach (var (index, member) in #Model.Members.Select((member, i) => (i, member)))
{
<div>#index - #member.anyProperty</div>
if(index > 0 && index % 4 == 0) { // display clear div every 4 elements
#: <div class="clear"></div>
}
}
For more info you can have a look at this link
Is there a reason you're not using CSS selectors to style the first and last elements instead of trying to attach a custom class to them? Instead of styling based on alpha or omega, use first-child and last-child.
http://www.quirksmode.org/css/firstchild.html
IndexOf seems to be useful here.
#foreach (myItemClass ts in Model.ItemList.Where(x => x.Type == "something"))
{
int currentIndex = Model.ItemList.IndexOf(ts);
#Html.HiddenFor(x=>Model.ItemList[currentIndex].Type)
...
All of the above answers require logic in the view. Views should be dumb and contain as little logic as possible. Why not create properties in your view model that correspond to position in the list eg:
public int Position {get; set}
In your view model builder you set the position 1 through 4.
BUT .. there is even a cleaner way. Why not make the CSS class a property of your view model? So instead of the switch statement in your partial, you would just do this:
<div class="#Model.GridCSS">
Move the switch statement to your view model builder and populate the CSS class there.
Very Simple:
#{
int i = 0;
foreach (var item in Model)
{
<tr>
<td>#(i = i + 1)</td>`
</tr>
}
}`
In case you want to count the references from your model( ie: Client has Address as reference so you wanna count how many address would exists for a client) in a foreach loop at your view such as:
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.DtCadastro)
</td>
<td style="width:50%">
#Html.DisplayFor(modelItem => item.DsLembrete)
</td>
<td>
#Html.DisplayFor(modelItem => item.DtLembrete)
</td>
<td>
#{
var contador = item.LembreteEnvolvido.Where(w => w.IdLembrete == item.IdLembrete).Count();
}
<button class="btn-link associado" data-id="#item.IdLembrete" data-path="/LembreteEnvolvido/Index/#item.IdLembrete"><i class="fas fa-search"></i> #contador</button>
<button class="btn-link associar" data-id="#item.IdLembrete" data-path="/LembreteEnvolvido/Create/#item.IdLembrete"><i class="fas fa-plus"></i></button>
</td>
<td class="text-right">
<button class="btn-link delete" data-id="#item.IdLembrete" data-path="/Lembretes/Delete/#item.IdLembrete">Excluir</button>
</td>
</tr>
}
do as coded:
#{ var contador = item.LembreteEnvolvido.Where(w => w.IdLembrete == item.IdLembrete).Count();}
and use it like this:
<button class="btn-link associado" data-id="#item.IdLembrete" data-path="/LembreteEnvolvido/Index/#item.IdLembrete"><i class="fas fa-search"></i> #contador</button>
ps: don't forget to add INCLUDE to that reference at you DbContext inside, for example, your Index action controller, in case this is an IEnumerable model.
I prefer to use this extension method:
public static class Extensions
{
public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self)
=> self.Select((item, index) => (item, index));
}
Source:
https://stackoverflow.com/a/39997157/3850405
Razor:
#using Project.Shared.Helpers
#foreach (var (item, index) in collection.WithIndex())
{
<p>
Name: #item.Name Index: #index
</p>
}