Select Random Content Spot - c1-cms

I have 10 content spots. I would like a page to show 1 of them randomly.
I have tried using the Function 'ShowContentSpots' - but that shows all of them & doesn't cycle through.
What's the best way of doing this?

You should edit the ShowContentSpots function and:
change the code so that only one item is retrieved (not all)
add some randomizer when retrieving a single item
A quick solution would be:
#if (!string.IsNullOrEmpty(ContentSpotIds))
{
var rand = new Random();
var spotIds = ContentSpotIds.Split(',').Select(f=>new Guid(f));
var spots = Data.Get<Content.ContentSpot>().Where(f=> spotIds.Contains(f.Id)).ToList();
if (spots.Any())
{
var spot = spots[rand.Next(spots.Count)];
<div class="spots">
<div class="spot">
#Html.Raw(spot.Content)
</div>
</div>
}
}
Please note that this is part of the original function's code.
Here I added:
var rand = new Random();
and modified this part:
var spot = spots[rand.Next(spots.Count)];
<div class="spots">
<div class="spot">
#Html.Raw(spot.Content)
</div>
</div>
This is just a quick sample. So when you refresh the page very quickly the spot might not change every time - because we create a new Random object every time we refersh the page.
To avoid this, initialize the Random object only once and somewhere else, and use it in this function.

Related

Mutating array within an array (Polymer iron-list)

I currently have an iron-list within another iron-list. The parent's data comes from a firebase-query element, and the child's data is computed from each parent item. The db structure and code looks a bit like this:
DB: [
category1: [
itemId1: {
price: 10,
title: "title"
}
]
]
<iron-list id="categoryList" items="{{categories}}" multi-selection as="category">
<template>
<div class="category-holder">
<iron-list id="{{category.$key}}" items="{{_removeExtraIndex(category)}}" as="item" selection-enabled multi-selection selected-items="{{selectedItems}}" grid>
<template>
<div class$="{{_computeItemClass(selected)}}">
<p>[[item.title]]</p>
<p>[[item.price]]</p>
</div>
</template>
</iron-list>
</div>
</template>
</iron-list>
After selecting any number of items, the user can tap on a fab to batch edit the price. This is where I'm having issues. I can't figure out how to access the correct child iron-list in order to call list.set...I'm currently trying the following very nasty method:
var categories = this.$.categoryList;
var categoryItems = categories.items;
(this.selectedItems).forEach(function(item) {
var index = item.itemId;
categoryItems.forEach(function(itemList, categoryIndex) {
if (itemList[index]) {
categories.set('item.' + categoryIndex + '.price', 10);
}
}, this);
}, this);
I'm iterating over the selected items in order to extract the item index and then iterating over the parent iron-list data (categoryItems) in order to check if the given item exists in that subset of data. If so, then I use the category index and attempt to call set on the parent iron-list using the given path to access the actual item I want to edit. As expected, this fails. Hopefully I've made myself clear enough, any help would be appreciated!
EDIT #1:
After much experimenting, I finally figured out how to correctly mutate the child iron-list:
(this.selectedItems).forEach(function(item) {
var list = this.$.categoryList.querySelector('#' + item.category);
var index = list.items.indexOf(item);
list.set(["items", index, "price"], 30);
}, this);
A couple of things worth noting. I'm using querySelector instead of the recommended this.$$(selector) because I keep running into a "function DNE" error. But now I have another problem...after calling the function, the value gets updated correctly but I get the following error:
Uncaught TypeError: inst.dispatchEvent is not a function
Here's a picture of the full error message:
I see the light, hopefully someone can help me out!
OK, I'll take a shot at this. I think the following happens, and I guess this based on how dom-repeat works:
var categories = this.$.categoryList;
var categoryItems = categories.items;
You take the variable that the iron-list is based on, but setting one array to another just creates a reference in javascript. As soon as you update categoryItems, you also update this.$.categoryList.items. When you later sets the new value, iron-list will do a dirty check and compare all subproperties, and because they are equal (because ... reference), the iron-list wont update the dom.
What you should do is to make sure it's a totally new copy and the way of doing that is to use JSON.parse(JSON.stringify(myArray)).
Further on, one major flaw I see in your code is that you're using querySelector to select an element, and then manipulate that. What you should do is to use this.categories and only that variable.
So your method should look something like:
// Get a freshly new array to manipulate
var category = JSON.parse(JSON.stringify(this.categories);
// Loop through it
category.forEach(category) {
// update your categoryList variable
}
// Update the iron list by notifying Polymer that categories has changed.
this.set('categories', category);

10 $digest() iterations reached. ng class?

I am using a ng-class function in an element in ng-repeat. And I am getting this error. I am trying to pick a random class from the array. How do I solve this?
<div class="col-lg-4" ng-repeat="client in allClients">
<div class="someClass" ng-class="getClass()">
//some data
{{client.name}}
</div>
</div>
And this is my JS code.
$scope.getClass = function() {
var classArray = ['infobox1', 'infobox2', 'infobox3', 'infobox4', 'infobox5', 'infobox6'];
return classArray[Math.floor(Math.random() * classArray.length)];
}
The error goes on.
Watchers fired in the last 5 iterations: [[{"msg":"getBackgroundClass()","newVal":"infobox4","oldVal":"infobox6"},{"msg":"getBackgroundClass()","newVal":"infobox4","oldVal":"infobox5"},{"msg":"getBackgroundClass()","newVal":"infobox1","oldVal":"infobox4"},{"msg":"getBackgroundClass()","newVal":"infobox5","oldVal":"infobox1"},{"msg":"getBackgroundClass()","newVal":"infobox1","oldVal":"infobox4"},{"msg":"getBackgroundClass()","newVal":"infobox6","oldVal":"infobox4"},{"msg":"getBackgroundClass()","newVal":"infobox1","oldVal":"infobox5"},{"msg":"getBackgroundClass()","newVal":"infobox1","oldVal":"infobox2"},{"msg":"getBackgroundClass()","newVal":"infobox1","oldVal":"infobox4"}],[{"msg":"getBackgroundClass()","newVal":"infobox6","oldVal":"infobox4"},{"msg":"getBackgroundClass()","newVal":"infobox1","oldVal":"infobox4"},{"msg":"getBackgroundClass()","newVal":"infobox4","oldVal":"infobox1"},{"msg":"getBackgroundClass()","newVal":"infobox5","oldVal":"infobox1"},{"msg":"getBackgroundClass()","newVal":"infobox4","oldVal":"infobox5"},{"msg":"getBackgroundClass()","newVal":"infobox2","oldVal":"infobox6"},{"msg":"getBackgroundClass()","newVal":"infobox6","oldVal":"infobox1"},{"msg":"getBackgroundClass()","newVal":"infobox6","oldVal":"infobox1"},{"msg":"getBackgroundClass()","newVal":"infobox4","oldVal":"infobox1"
How often do you want to pick a random class? Currently, you have created an infinite loop because angular keeps noticing the class changed, runs a digest cycle to watch out for other changes, finds that the class changed again, runs a digest cycle to watch out for other changes, finds that the class changed again, and so on ...
You way want to pick the random value once, and stick with it for a while:
$scope.randomClass = classArray[Math.floor(Math.random() * classArray.length)]
(feel free to execute the above code as often as you want to see a new value)
Here is the working example
http://plnkr.co/edit/pl3W5uNofz123EJZnMT8
I made a classes array when I have the array with all my values in allClients, and then used that array to assign the classes
$scope.allClients = [{name: 'client'}, {name: 'client2'}, {name: 'client3'}];
var classes = $scope.allClients.map(function(client) {
var classArray = ['infobox1', 'infobox2', 'infobox3', 'infobox4', 'infobox5', 'infobox6'];
return classArray[Math.floor(Math.random() * classArray.length)];
});

Angular array dupe error when adding new objects to an array

I have an alert system, and the user needs to be able to create an unlimited number of alerts and add an unlimited number of triggers to each alert.
For instance the user may have an alert called "When prices change on my cars". They need to be able to create a trigger of the same type ("Price Change") for each of the cars they want to follow.
What follows is a stripped-down version, only dealing with the triggers.
Here's a plunker - just press Add twice and you'll see the issues.
JS
// the array of trigger possibilities
$scope.triggerOptions = [
{id : 0, type: 1, action: "Status Update For Brand"},
{id : 1, type: 2, action: "Price Changed for "}
];
// the model for the select element
$scope.selected = $scope.triggerOptions[0];
// this array will hold all the triggers created
$scope.triggers = [];
// add some indexes to the new trigger object, then add it to triggers
$scope.addTrigger = function() {
var newTrigger = $scope.selected;
var newID = $scope.triggers.length;
var alertID = 0; // todo: apply alert id
newTrigger.triggerID = newID;
newTrigger.alertID = alertID;
$scope.triggers.push(newTrigger);
};
HTML
<select ng-options = "option.action for option in triggerOptions track by option.id" ng-model = "selected"></select>
<button ng-click="addTrigger()">Add</button>
<div ng-repeat="trigger in triggers track by triggerID" class="alert-tool-action-box">
<div ng-show="trigger.type==1">
<div>{{trigger.action}}</div>
</div>
<div ng-show="trigger.type==2">
<div>{{trigger.action}}</div>
</div>
</div>
Issues
When I add more than one trigger, only the first trigger is shown, but I get a "dupes" error message (which suggests I add a 'track by', but I already did).
When I add two triggers of the same type in a row, the triggerID is updated to the new triggerID for both triggers:
one trigger:
[{"id":0,"type":1,"action":"Status Update For Brand","triggerID":0,"alertID":0}]
two triggers:
[
{"id":0,"type":1,"action":"Status Update For Brand","triggerID":1,"alertID":0},
{"id":0,"type":1,"action":"Status Update For Brand","triggerID":1,"alertID":0}
]
I should be able to see each trigger as I add them, even if they're the same as the one before.
Your array of objects can't contain duplicates (Error: [ngRepeat:dupes]), now these two objects are equal. This can be solved using track by, which must be unique, so triggerID can't be used here. You can always use track by $index (provided by ng-repeat directive) if no unique property is available.
About your second problem, you are always pushing the same object, as you only have 1 instance of the $scope.
To solve that, you just need to clone (or copy) the $scope.selected, like this:
var newTrigger = angular.copy($scope.selected);
Hope it helps!
You ng-repeat show only one representant of you array because you track by triggerID which don't exist (angular should search it on the $scope and return undefined each time it call it). The good way to call triggerID will be trigger.triggerID. So :
<div ng-repeat="trigger in triggers" class="alert-tool-action-box">
....
</div>
or if you want to use track by :
<div ng-repeat="trigger in triggers track by trigger.triggerID class="alert-tool-action-box">
....
</div>
Your second issue is linked to the fact that javascript pass object by reference and not by value. You don't create a new object. You just pass the same and change its value. It's why you have all your object that are updated with the same id.
So you can use angular.copy() to make it different object. Something like :
$scope.addTrigger = function() {
var newTrigger = angular.copy($scope.selected);
var newID = $scope.triggers.length;
var alertID = 0; // todo: apply alert id
newTrigger.triggerID = newID;
newTrigger.alertID = alertID;
$scope.triggers.push(newTrigger);
};

how to retrieve data nested in two collections from firebase with angular

I'm new in Angular - Firebase development, and I am having problems to understand how to retrieve data nested in two collections.
I have a collection named "Orders", which includes a field call "auth", which is the user ID, and I have another collection that is the "User Profile", wich it's $id is the value of "auth". Inside the User Profile I have a field named roomNumber, and it's content I that I want to retrieve every time I read, in ng-repeat of the Orders.
In my view I was trying to do something like this :
<tr ng-repeat="item in items | filter: searchKeyword ">
<td align="left">{{item.$id}} - {{roomNumber(item.$id)}}</td></tr>
roomNumber is a function in my controller
$scope.roomNumber = function(id) {
var rootRef = new Firebase("https://xxxx-fire-yyyy.firebaseio.com/userProfile"+ '/' + id);
$scope.userdet = $firebaseArray(rootRef);
rootRef.on("value", function(rootSnapshot) {
var key = rootSnapshot.key();
var childKey = rootSnapshot.child("room").val();
console.log("room ", childKey)
});
return childKey
}
When I run this code and see results in my js console, strange things happend:
1. It repeat a lot of times
2. I can never get the childKey value
I have been reading Firebase documentation, but really I do not understand how to do this "silly" thing, does anybody give me a tip of how to do it?
When you bind a function to the $scope and call it within the html it expects to get an answer back right away when called. So when you query firebase and it takes its sweet time getting you back an answer, angularjs has already gotten an answer of undefined from the function.
So what is happening is that you are registering a callback when you provide the function to rootRef.on and then right after you register the callback you are returning the value of childKey. Unfortunately, childKey only gets set by the callback function (which firebase hasn't executed yet). Therefore angularjs gets an answer of undefined from your roomNumber function.
In order to make this work, you are going to have to get the room numbers beforehand and then probably add them to each of your items in $scope.items then use
<td align="left">{{item.$id}} - {{item.room}}</td></tr>
instead of
<td align="left">{{item.$id}} - {{roomNumber(item.$id)}}</td></tr>
To load all the room numbers you could call some function like this one after $scope.items has loaded
for (var i = 0; i < $scope.items.length; i++) {
var rootRef = new Firebase("https://xxxx-fire-yyyy.firebaseio.com/userProfile"+ '/' + $scope.items[i].$id);
$scope.userdet = $firebaseArray(rootRef);
rootRef.on("value", function(rootSnapshot) {
var key = rootSnapshot.key();
var childKey = rootSnapshot.val().room;
$scope.items[i].room = childKey;
});
}
It would change each of the items to have a reference to the room. Unfortunately, that list wouldn't update as the data updates, so the better solution would be to do that same query in whatever function was getting your items from the server and add the room to each item as it was being added to the items list.
To fix the issue with childKey not reading you need to use this:
var childKey = rootSnapshot.val().room;
instead of this:
var childKey = rootSnapshot.child("room").val();
console.log("room ", childKey)
Reference: https://www.firebase.com/docs/web/guide/retrieving-data.html

BackboneJS - get specific value from Model using .max

So I have this:
var competitionModel = new Competition.CompetitionModel();
competitionModel.contest_id = this.contest_id;
this.insertView('.comp', new Competition.View({model: competitionModel}));
competitionModel.fetch();
So far so good, the Model and its (selected) values are getting display in the <div class="comp">.
Now I want to get a specific value from the same Model, in this case profile_image and it has to be the MAX value from the model. I read something about .max()-method but I dont know how to use it
I have this structure:
<div class="image"></div>
<div class="comp"></div>
1) is it possible? 2) can I use the same methods? like this.insertView('.image', blablab)
So, could anyone help me out?
Ok, judging by your comment the property is an array of things.
You cannot use the backbone max (which only applies to collections) but you can use the underscore max (they are the same thing, in the end, the former is a wrapper for the latter but let's not go into the details). You can see the collection .max() in action here.
You should be able to do something like this:
var max = _.max(competitionModel.get("property"));
Eventually you can pass a function to transform values:
var max = _.max(competitionModel.get("property"), function (element) {
// element is a single item in the list, return a number here.
});
Alternatively you can also use the underscore wrapper like this:
var max = _(competitionModel.get("property")).max(function (e) { ... });
More on max() can be found in the Underscore Docs.

Resources