2sxc: foreach how to identify first element - dotnetnuke

In a foreach loop, how do I identify the first element? For example, in the snippet below, can I identify the Content item that is the first (or last) item?
#foreach(var Content in AsDynamic(Data["Default"]))
{
??
}

You can do something like this:
#{int i = 1;}
#foreach(var Content in AsDynamic(Data["Default"]))
{
if(i==1)
{
//this is first element
}
i++;
}

Another option is to compare the current item to the First() or Last(). But you may run into some surprises because of the Dynamic wrapper caused by AsDynamic. So my pseudocode recommendation is a bit like this
var firstI = Data["Default"].FirstOrDefault(); // returns null if nothing
var lastI = Data["Default"].LastOrDefault(); // null...
#foreach(var Content in AsDynamic(Data["Default"]))
{
var isFirst = AsEntity(Content) == firstI; // unwrap and compare
//etc.
}

Related

Infinite Digest Loop in AngularJS filter

I have written this custom filter for AngularJS, but when it runs, I get the infinite digest loop error. Why does this occur and how can I correct this?
angular.module("app", []).
filter('department', function(filterFilter) {
return function(items, args) {
var productMatches;
var output = [];
var count = 0;
if (args.selectedDepartment.Id !== undefined && args.option) {
for (let i = 0; i < items.length; i++) {
productMatches = items[i].products.filter(function(el) {
return el.Order__r.Department__r.Id === args.selectedDepartment.Id;
});
if (productMatches.length !== 0) {
output[count] = {};
output[count].products = productMatches;
output[count].firstProduct = items[i].firstProduct;
count++;
}
}
}
return output;
};
}).
This is the relevant HTML:
<tr class='destination' ng-repeat-start='pickupAccount in pickupAccounts | department : {"selectedDepartment": selectedDepartment, "option": displayExclusive }'>
<!-- td here -->
</tr>
displayExclusive is boolean.
I have written this custom filter for AngularJS, but when it runs, I get the infinite digest loop error.
Keep in mind that filter should return array of the same object structure. When we activate filter, it fires digest cycle that will run over our filter again. If something changed in output list - fires new digest cycle and so on. after 10 attempts it will throw us Infinite Digest Loop Exception
Testing
This empty filter will works (100%). Actually we do nothing here but return the same object that filter receives.
filter('department', function(filterFilter) {
return function(items, args) {
var output = items;
return output;
};
})
Now the main idea is: write some condition to push to output objects from input list a.e. items based on some if statement, a.e.
var output = [];
if (args.selectedDepartment.Id !== undefined && args.option) {
angular.forEach(items, function(item) {
if(<SOME CONDITION>) {
output.push(item);
}
});
}
By this way it will work too.
our case:
we have this logic:
productMatches = items[i].products.filter(function(el) {
return el.Order__r.Department__r.Id === args.selectedDepartment.Id;
});
if (productMatches.length !== 0) {
output[count] = {};
output[count].products = productMatches;
output[count].firstProduct = items[i].firstProduct;
count++;
}
Here we completely modified object that has been stored in output.
So next digest cycle our items will change again and again.
Conclusion
The main purpose of filter is to filter list and not modify list object content.
Above mentioned logic you wrote is related to data manipulation and not filter. The department filter returns the same length of items.
To achieve your goal, you can use lodash map or underscorejs map for example.
This happens when you manipulate the returned array in a way that it does not match the original array. See for example:
.filter("department", function() {
return function(items, args) {
var output = [];
for (var i = 0; i < items.length; i++) {
output[i] = {};
output[i] = items[i]; // if you don't do this, the next filter will fail
output[i].product = items[i];
}
return output;
}
}
You can see it happening in the following simplified jsfiddle: https://jsfiddle.net/u873kevp/1/
If the returned array does have the same 'structure' as the input array, it will cause these errors.
It should work in your case by just assigning the original item to the returned item:
if (productMatches.length !== 0) {
output[count] = items[i]; // do this
output[count].products = productMatches;
output[count].firstProduct = items[i].firstProduct;
count++;
}
output[count] = {};
Above line is the main problem. You create a new instance, and ng-repeat will detect that the model is constantly changed indefinitely. (while you think that nothing is changed from the UI perspective)
To avoid the issue, basically you need to ensure that each element in the model remains the 'same', i.e.
firstCallOutput[0] == secondCallOutput[0]
&& firstCallOutput[1] == secondCallOutput[1]
&& firstCallOutput[2] == secondCallOutput[2]
...
This equality should be maintained as long as you don't change the model, thus ng-repeat will not 'wrongly' think that the model has been changed.
Please note that two new instances is not equal, i.e. {} != {}

AngularJS mysql filter multiple element

Hi this is my hotel project. But I'm having a problem with a filter.
I want to filter the data in the amenities column.
This is: my fiddle
It works if you select a single checkbox but it does not work if you select multiple checkboxes.
I suspect the problem arises from indexof instead of what I can use. What method should I follow?
How to change this line: indexOf(x);
This is my bad code:
//PROBLEM FILTER HERE
$scope.am_en = function()
{
//get input value
x = $(".hosting_amenities input:checkbox:checked").map(function(){return $(this).val();}).get();
//filter
$scope.ot_Filter = function (location) {
return location.amenities.indexOf(x) !== -1;
};
}
The problem is indeed caused by the function $scope.am_en, in the declaration of the inner function $scope.ot_Filter
When you select multiple checkboxes, your x variable is an array of objects, so you should do a loop and can create a variable to check whether the element should be shown or not. You can do it as follows:
$scope.ot_Filter = function (location) {
var shouldBeShown = false;
for (var i = 0; i < x.length; i++) {
if (location.amenities.indexOf(x[i]) !== -1) {
shouldBeShown = true;
break;
}
}
return shouldBeShown;
};
I modified your jsfiddle, so that you can see this solution working properly.

delete multiple selected items from the list angular js

I am trying to delete the one or more selected items in the list in angular js.
I tried to add the items in the following way:
$scope.selectedNodes = [];
$scope.addItem = function(e) {
$scope.selectedNodes.push({
id :$scope.selectedNodes.length + 1,
Nodeid: e.item.id,
title: e.item.text});
$scope.$apply();
}
html is as below:
<select ng-model="selectedItems" multiple ng-multiple="true">
<option ng-repeat="node in selectedNodes" value="{{node}}">{{node}}</option>
</select>
<button ng-click="remove(selectedItems)" type="submit">
Remove
</button>
The above html is listing fine with all the items.
Now I am trying to delete one or more items from the list, so the code I have written is:
$scope.remove = function (nodes) {
alert(nodes); // it's giving the selected records info, no problem with it
angular.forEach(nodes, function (node) {
var index = $scope.selectedNodes.indexOf(node);
alert(index) //problem here, it's always -1
$scope.selectedNodes.splice(index, 1);
});
};
The above code is removing the last item if one item is selected. And if more than one is selected, let's say two, it's then removing the last two records.
The index value is always -1 for any no. of iterations in the foreach loop.
Could anyone please help with the above code to delete one or more selected records and the list should get refreshed. No problem with refreshing for the above code.
I tried as you mentioned below, but no luck.
$scope.remove = function (nodes) {
alert(nodes); // it's dispalying correct results
for(var i = 0; i< nodes.length; i++)
{
alert(nodes.length); // correct result, no problem
alert(nodes[i]); //correct result, no problem
alert(nodes[i].Nodeid); // problem, value Nodeid is Undefined
for (var idx = 0; idx < $scope.selectedNodes.length; idx++) {
alert($scope.selectedNodes[idx].Nodeid);
if ($scope.selectedNodes[idx].Nodeid == nodes[i].Nodeid) {
$scope.selectedNodes.splice(idx, 1);
break;
}
}
};
};
You're trying to locate the node using indexOf, which compares values using the strict '===' operator, and you're trying to compare objects.
I think it would be easier for you to use one of the many libraries outthere for collection manipulation (lodash, underscore, etc) but if you want to do it as you were doing then this is the code:
$scope.remove = function (nodes) {
angular.forEach(nodes, function (node) {
for (var idx = 0; idx < $scope.selectedNodes.length; idx++) {
if ($scope.selectedNodes[idx].Nodeid == node.Nodeid) {
$scope.selectedNodes.splice(idx, 1);
break;
}
}
});
};
If the number of nodes to delete is very high and you're concerned about optimization then you can iterate the selectedNodes array in inverse order and be deleting the node if it's in the nodesToDelete collection. The code structure is similar, just iterating outside the selectedNodes and inside the nodesToDelete.

Using variable A to control variable B

I have a function called FarmClick, and I want it to check the current MovieClip's array. For example:
var farmSpot1:("id",int,int);
var farmSpot2...
I need the clickEvent to capture the MovieClips name and check it's array for the 0th element to see whether it's empty, or not...
My code so far:
public function FarmClick(event: MouseEvent): void {
var CurrentSlot = event.currentTarget.name
if ([event.currentTarget.name[0]] = "empty") {
stage.addChild(menu);
menu.x = 400;
menu.y = 90;
this.menu.buyCornBtn2.addEventListener(MouseEvent.CLICK,buyCorn2);
} else if (farmEmpty == true && itemSelected != "none") {
selected();
} else if (farmEmpty == false) {
farmHarvest();
}
}
It took me a minute to figure out just exactly what it was that you're asking.
So basically, your movie clip's name would be farmSpot[someNumber] and you have an array with the same name?
in that case, to reference the array variable, you need to use the this keyword.
you can add any array subscripts after you've referenced it like so:
private var farmSpot1:Array = ["id", int, int];
public function FarmClick(event:MouseEvent): void
{
if (this[event.currentTarget.name][0] == "") // wasnt sure if you meant the string "empty" or just an empty string, so i just put an empty string
{
// do whatevs
}
}

Testing Angular Filter That Returns An Array with Jasmine

So, I'm having issues testing an angular filter that takes an array that has previously been sorted by a group property. It uses a flag property to indicate that the item is the first observation of that group, and then false for subsequent observations.
I'm doing this to have a category header in the UI with an ng-repeat directive.
When I test the filter, the output does not return the array with the flags unless I create new objects for the return array. This is a problem, because it causes an infinite loop when running in a webpage. The code works in the webpage when it just adds a flag property to the input object.
Is there some additional step I should be taking to simulate how angular handles filters so that it outputs the proper array?
This is what my test looks like right now.
describe('IsDifferentGroup', function() {
var list, itemOne, itemTwo, itemThree;
beforeEach(module("App.Filters"));
beforeEach(function () {
list = [];
itemOne = new ListItem();
itemTwo = new ListItem();
itemThree = new ListItem();
itemOne.group = "A";
itemTwo.group = "B";
itemThree.group = "C";
list.push(itemOne);
list.push(itemOne);
list.push(itemOne);
list.push(itemOne);
list.push(itemTwo);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
});
it('should flag the items true that appear first on the list.', (inject(function (isDifferentGroupFilter) {
expect(list.length).toBe(10);
var result = isDifferentGroupFilter(list);
expect(result[0].isDifferentGroup).toBeTruthy();
expect(result[1].isDifferentGroup).toBeFalsy();
expect(result[4].isDifferentGroup).toBeTruthy();
expect(result[5].isDifferentGroup).toBeTruthy();
expect(result[6].isDifferentGroup).toBeFalsy();
expect(result[9].isDifferentGroup).toBeFalsy();
})));
});
And here is something like the code with the filter:
var IsDifferentGroup = (function () {
function IsDifferentGroup() {
return (function (list) {
var arrayToReturn = [];
var lastGroup = null;
for (var i = 0; i < list.length; i++) {
if (list[i].group != lastGroup) {
list[i].isDifferentGroup = true;
lastAisle = list[i].group;
} else {
list[i].isDifferentGroup = false;
}
arrayToReturn.push(list[i]);
}
return arrayToReturn;
});
}
return IsDifferentGroup;
})();
Thanks!
I figured out my issue.
When I was passing the items into the list, I just pushed a pointer to an item multiple times. I was not passing in unique objects so the flag was being overridden by the following flag in the array(I think). So, I just newed up 10 unique objects using a loop, pushed them into the array and ran it through the filter. And it worked.
I'm not entirely sure my analysis is correct about the override, because itemTwo was not being flagged as unique when it was the only itemTwo in the array. But the test is working as I would expect now so I'm going to stop investigating the issue.

Resources