I've seen a couple examples out there but I can't get it to apply to what I am doing. I know it has to be something silly I am missing.
My report objects are sorted like this by description
report { "description" }
a,b,c,d
e,f,g,h
I want
a,d,g,j
b,e,h,k
c,f,i,l
<div class="row">
<div data-ng-repeat="report in reports | orderBy:['description']" >
<div class="col-xs-3"> {{report.description}}</div>
</div>
...
I've tried chunking the data and several other approaches I've seen on here and I get a mix of results. Every 5th row starts anew with the above code, but my order is across (Horizontal) but I need it in 4 columns alphabetical down (Vertical) ....
It certainly can't be as hard as I am making it...
Are you looking for this below logic?
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
</head>
<body>
<div ng-app="app" ng-controller="ctrl">
{{convertedData}}
</div>
<script src="../lib/angular.js"></script>
<script>
var app = angular.module('app', []);
app.controller('ctrl', function ($scope) {
$scope.data = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];
$scope.convertedData = {};
var length = $scope.data.length;
var rows = length / 3;
for (var j = 0; j < rows; j++)
{
$scope.convertedData[j] = [];
}
for(var i=0;i<length;i++)
{
$scope.convertedData[i%3].push($scope.data[i]);
}
});
</script>
</body>
</html>
you can use the following code to order your reports
$scope.reports = [{ description: 'a' }, { description: 'b' }, { description: 'c' }, { description: 'd' }, { description: 'e' }, { description: 'f' }, { description: 'g' }, { description: 'h' }, { description: 'i' }, { description: 'j' }, { description: 'k' }, { description: 'l' }];
$scope.orderedReports = [];
var j = 0;
var i = j;
while ($scope.reports.length > $scope.orderedReports.length) {
$scope.orderedReports.push($scope.reports[i]);
i = i + 3;
if (i + 1 > $scope.reports.length) {
j = j + 1;
i = j;
}
}
after ordering the data ng-repeat will show reports the way you want them.
<div data-ng-repeat="report in reports" >
<div class="col-xs-3">
{{report.description}}
</div>
</div>
Related
I am trying to use a Font Awesome icon to appear next the string of an array. Is it possible? Would I need a library for this?
Thanks a lot
I have tried these (inside and outside the "", which all break my code.
e.innerHTML = '<i class="fas fa-venus" aria-hidden="true"></i>'
e.append('<i class="fas fa-mars" aria-hidden="true"></i>');
var quotesAm = [
/*I need the icons to appear before each of these words below*/
"Hemlaɣk.<br>Hemlaɣkem."
]
let uniqueRandomGenerator = n => {
let set = new Set() // Use Set to remove any duplicates as keep adding #
while (set.size < n) set.add(Math.floor(Math.random() * n)) // Keep adding #
return Array.from(set)
}
let randomQuotes = uniqueRandomGenerator(quotesAm.length), last = 0
function newQuoteAm() {
document.getElementById('amQuoteDisplay').innerHTML = quotesAm[randomQuotes[last]];
last = last == randomQuotes.length - 1 ? 0 : last + 1
}
<h1 class="tamazight-tifinaghe">Amaziɣ Daily</h1><br>
<div id="amQuoteDisplay">
<!--Amaziɣ quotes display here-->
</div>
<div align="left">
<button onclick="newQuoteAm()">Next</button>
</div>
</div>
</div>
<script src="testAm.js"></script>
<script src="https://kit.fontawesome.com/3cb9f76276.js"></script>
You can make your array of quotes to be array of objects with icon specified for each element:
var quotesAm = [
{ label: 'a', icon: 'fa-venus' },
{ label: 'b', icon: 'fa-mars' },
{ label: 'c', icon: 'fa-box' },
{ label: 'd', icon: 'fa-bath' },
{ label: 'e', icon: 'fa-bolt' },
{ label: 'f', icon: 'fa-at' },
]
let uniqueRandomGenerator = n => {
let set = new Set() // Use Set to remove any duplicates as keep adding #
while (set.size < n) set.add(Math.floor(Math.random() * n)) // Keep adding #
return Array.from(set)
}
let randomQuotes = uniqueRandomGenerator(quotesAm.length), last = 0
function newQuoteAm() {
let quote = quotesAm[randomQuotes[last]]
document.getElementById('amQuoteDisplay').innerHTML = `<span><i class="fas ${quote.icon}" aria-hidden="true"></i> ${quote.label}</span>`
last = last == randomQuotes.length - 1 ? 0 : last + 1
}
<h1 class="tamazight-tifinaghe">Amaziɣ Daily</h1><br>
<div id="amQuoteDisplay">
<!--Amaziɣ quotes display here-->
</div>
<div align="left">
<button onclick="newQuoteAm()">Next</button>
</div>
<script src="https://kit.fontawesome.com/3cb9f76276.js"></script>
If you want to have only one icon then use the same approach just at the newQuoteAm function hard that class set in there and keep your array of strings not objects.
I am currently making a quiz in Javascript that randomizes questions and answers. I have the questions randomizing but how do I add the answers to go along with a certain question? I also want each answer to be placed in a div of its own, like so: http://imgur.com/a/l9w9j
Here's the code I have so far:
var display = document.getElementById("questions");
var questions = ['What is the weather like?',
'What time of day is it?',
'Whats your favourite music?',
'Which season is your favourite?',
'What colour are your eyes?'];
var questionTracker = [];
var questionAmount = 1;
// Iterate however many times
for (var i = 0; i < questionAmount; i++) {
// Keep creating random numbers until the number is unique
do {
var randomQuestion = Math.floor(Math.random() * questions.length);
} while (existingQuestions());
display.innerHTML += questions[randomQuestion] + '<br>';
// Add the question to the tracker
questionTracker.push(randomQuestion);
}
// If the current random number already exists in the tracker, return true
function existingQuestions() {
for (var i = 0; i < questionTracker.length; i++) {
if (questionTracker[i] === randomQuestion) {
return true;
}
}
return false;
}
And my HTML:
<div id="questions">
</div>
<div id="answers">
<div class="answers-left">
<div class="answer1" tabIndex="1">Sunny</div>
<div class="answer2" tabIndex="2">Raining</div>
</div>
<div class="answers-right">
<div class="answer3" tabIndex="3">Cloudy</div>
<div class="answer4" tabIndex="4">Windy</div>
</div>
<div class="clear"></div>
</div>
Her you can use of object instead of array
var questionData= {
"questions":[
{
"question":"this is hard question to answer",
"answers":[
"yes","no","why not","none"
]
},
{
"question":"this is 2nd hard question to answer",
"answers":[
"yes","no","why not","none"
]
}
]
}
questionData.map(function(question){
//Here you can write the dom structure that you like
})
You can store your questions and answers inside an array of objects
Each object hold a question and an answers property. answers is an array that contains each possible answers.
The following code will take a random question using Math.random() to find a random index. With this index, you can select the object in the array and then select the question and answers.
I added some CSS to add the desired effect. This can be improved with the colors/size/margins/... you want
var questionElement = document.getElementById("questions");
var answersElements = document.getElementsByClassName("answer");
var data = [{
question: 'What is the weather like?',
answers: ['Sunny', 'Raining', 'Cloudy', 'Windy']
}, {
question: 'What time of day is it?',
answers: ['Morning', 'Lunch', 'Evening', 'Night']
}];
var randomIndex = Math.floor(Math.random() * data.length);
questionElement.innerHTML = data[randomIndex].question;
for (let i = 0; i < answersElements.length; i++) {
answersElements[i].innerHTML = data[randomIndex].answers[i];
}
.answer {
display: inline-block;
background-color: #00BCD4;
margin: 1em;
}
<div id="questions">
</div>
<div id="answers">
<div class="answers-left">
<div class="answer" tabIndex="1">Sunny</div>
<div class="answer" tabIndex="2">Raining</div>
</div>
<div class="answers-right">
<div class="answer" tabIndex="3">Cloudy</div>
<div class="answer" tabIndex="4">Windy</div>
</div>
<div class="clear"></div>
</div>
Why not add the answers to the questions in an object array?
var display = document.getElementById("questions");
var answers = document.getElementById("answers");
var answersLeft = document.getElementById("answers-left");
var answersRight = document.getElementById("answers-right");
var questions = [{
"q": "What is the weather like?",
"a": [
"Sunny",
"Raining",
"Cloudy",
"Windy"
]
},
{
"q": "What time of day is it?",
"a": [
"Sunny",
"Raining",
"Cloudy",
"Windy"
]
},
{
"q": "Whats your favourite music?",
"a": [
"Sunny",
"Raining",
"Cloudy",
"Windy"
]
},
{
"q": "Which season is your favourite?",
"a": [
"Sunny",
"Raining",
"Cloudy",
"Windy"
]
},
{
"q": "What colour are your eyes?",
"a": [
"Sunny",
"Raining",
"Cloudy",
"Windy"
]
}
];
var questionTracker = [];
var questionAmount = 1;
// Iterate however many times
for (var i = 0; i < questionAmount; i++) {
// Keep creating random numbers until the number is unique
do {
var randomQuestion = Math.floor(Math.random() * questions.length);
} while (existingQuestions());
display.innerHTML += questions[randomQuestion].q + '<br>';
var answersToQ = questions[randomQuestion].a;
for (var j = 0; j < answersToQ.length; j++) {
var answer = "<p>" + answersToQ[j] + "</p>";
if (j % 2 === 0) {
answersLeft.innerHTML += answer;
} else {
answersRight.innerHTML += answer;
}
}
// Add the question to the tracker
questionTracker.push(randomQuestion);
}
// If the current random number already exists in the tracker, return true
function existingQuestions() {
for (var i = 0; i < questionTracker.length; i++) {
if (questionTracker[i] === randomQuestion) {
return true;
}
}
return false;
}
<style type="text/css">
#answers-left {
position: relative;
float: left;
width: 50%;
}
#answers-right {
position: relative;
float: right;
width: 50%;
}
#answers p {
background-color: blue;
width: 50%;
text-align: center;
color: #fff;
cursor: pointer;
}
</style>
<div id="questions">
</div>
<div id="answers">
<div id="answers-left">
</div>
<div id="answers-right">
</div>
</div>
Here is example that i made for you with following code.
Sorry but i did not have time to make ccs rules, but you can see that questions are mixed and answers for them all mixed all so.
http://devel.vis25.com/test.php
I recommend you to use something like this, for my example you will need Jquery and Jquery templates
Here is link to Jquery download jquery tempaltes
Here is example of you'r tempaltes and html.
<html>
<head>
<script src="https://devel.vis25.com//Vendors/JqueryUI/external/jquery/jquery.js"></script>
<script src="http://devel.vis25.com/Vendors/jquery.tmpl.min.js"></script>
</head>
<body onload="RenderQuestions();">
<div id="Questions"></div>
<script id="Question-Tempalte" type="text/x-jQuery-tmpl">
<div class="Question" id=question-"${ID}">
<div class="Question-text">${QuestionText}</div>
<div class="Question-answer-container" id="Question-answer-container-${ID}"></div>
</div>
</script>
<script id="Answer-Tempalte" type="text/x-jQuery-tmpl">
<div class="answer" id="answer-${ID}">
<div class="answer-text" tabIndex="${ID}">${answerText}</div>
</div>
</script>
</body>
</html>
with javascript do something like this.
//Function that is called in body 'onload' event.
function RenderQuestions(){
//Array of you'r questions as json objects
var questions = [
{ ID : '1', QuestionText : 'What is the weather like?' },
{ ID : '2', QuestionText : 'What time of day is it?' },
{ ID : '3', QuestionText : 'Whats your favourite music?' },
{ ID : '4', QuestionText : 'Which season is your favourite?' },
{ ID : '5', QuestionText : 'What colour are your eyes?' },
];
//Call shuffle function for your questions, so they are mixed randomly.
var ShuffledQuestions = shuffle( questions );
//Loop true all of your questions and render them inside of your questions <div>
//Allso call functions 'RenderAnswers()' by question id value[ 'ID' ].
$.each(ShuffledQuestions, function(index, value){
$( '#Question-Tempalte' ).tmpl( value ).appendTo( '#Questions' );
RenderAnswers( value[ 'ID' ] );
});
}
//Shuffle function return randomly mixed array.
function shuffle( array ) {
var currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
//RenderAnswers function takes QuestionID as argument so we can render answer elements for right questions, and we have right answers.
function RenderAnswers( QuestionID ){
var Answers = [];
//Check which question are we rendering.
//Answers for question ID 1 ( 'What is the weather like?' ).
if( QuestionID == 1){
Answers = [
{ AnswersID : 1 , answerText : 'Sunny' },
{ AnswersID : 2 , answerText : 'Raining'},
{ AnswersID : 3 , answerText : 'Cloudy'},
{ AnswersID : 4 , answerText : 'Windy'},
];
}
//Answers for question ID 2 ( 'What time of day is it?' ).
if( QuestionID == 2){
Answers = [
{ AnswersID : 1 , answerText : '8:00' },
{ AnswersID : 2 , answerText : '12:00'},
{ AnswersID : 3 , answerText : '18:00'},
{ AnswersID : 4 , answerText : '00:00'},
];
}
//Answers for question ID 3 ( 'Whats your favourite music?' ).
if( QuestionID == 3){
Answers = [
{ AnswersID : 1 , answerText : 'Rock' },
{ AnswersID : 2 , answerText : 'pop'},
{ AnswersID : 3 , answerText : 'rap'},
{ AnswersID : 4 , answerText : 'EDM'},
];
}
//Answers for question ID 4 ( 'Which season is your favourite?' ).
if( QuestionID == 4){
Answers = [
{ AnswersID : 1 , answerText : 'Summer' },
{ AnswersID : 2 , answerText : 'Winter'},
{ AnswersID : 3 , answerText : ''},
{ AnswersID : 4 , answerText : ''},
];
}
//Answers for question ID 5 ( 'What colour are your eyes?' ).
if( QuestionID == 4){
Answers = [
{ AnswersID : 1 , answerText : 'blue' },
{ AnswersID : 2 , answerText : 'brown'},
{ AnswersID : 3 , answerText : 'green'},
{ AnswersID : 4 , answerText : ''},
];
}
//Shuffle answers.
var ShuffledAnswers = shuffle( Answers );
//Renders answer elements for question.
$( '#Answer-Tempalte' ).tmpl( ShuffledAnswers ).appendTo( '#Question-answer-container-'+QuestionID );
}
Hope i was able to help you, and feel free to ask anything is i did not understand your question right !
Best regards,
Vis25
I have the following array of objects:
payload: [
{name: one},
{name: two,
values: {name: five}
},
{name: three},
{name: four}
]
I loop through this in a recursive way, because this depth of the data can change anytime. So name: five can have there own values again.
Now when I loop through the values of an object, I want the name of the parent object. So for name: five I want to get two in a method.
Is there any way to obtain this name?
I use vue.js a a Javascript library.
This is my loop:
<ul>
<div class="row">
<li v-if="value.name" class="col-md-3 indent" #click="toggle">
{{value.name}}:
</li>
<li v-else class="col-md-3 indent" #click="toggle">
{{value.type}}:
</li>
</div>
<div v-show="open" v-if="isValue">
<codeblock-value
v-for="value in value.values"
:value="value">
</codeblock-value>
</div>
</ul>
And I render this loop like this in my parent file:
<div class="row" v-for="value in payload.values">
<codeblock-value
:value="value">
</codeblock-value>
</div>
Keep in mind that there can be multiple objects with values.
function recurse(parentName, obj) {
console.log("Parent name is: " + parentName);
console.log("Obj name is: " + obj.name);
if(obj.values) {
recurse(obj.name, obj.values);
}
}
recurse(payload[1]);
If you can change your payload structure slightly it would make life a bit easier.
JSFIDDLE
JS
var payload = {
name: "payload",
values: [{
name: "one"
}, {
name: "two",
values: [{
name: "five"
}]
}, {
name: "three"
}, {
name: "four"
}]
};
function dig(parent) {
console.log(parent.name);
if (parent.hasOwnProperty('values') && Array.isArray(parent.values)) {
for(var x = 0, len = parent.values.length; x < len; x++){
dig(parent.values[x]);
}
}
}
dig(payload);
UPDATE FOR VUE.JS
Again, changing the data structure allows you to access the parent. In this example, i dynamically generate the test data so that each child node references its parent (I threw in some randomness to generate folders or not).
JSFIDDLE
Data generation JS
var data = {
name: 'My Tree',
children: []
}
var maxDepth = 4;
function createChild(parent, currentDepth){
var childrenValues = ['hello', 'wat', 'test'];
var createChildFolderChance = 0.5;
for(var x = 0, len = childrenValues.length; x < len; x++){
var child = {
name: childrenValues[x],
parent: parent
}
if(Math.random() < createChildFolderChance && currentDepth < maxDepth){
child.children = [];
currentDepth++;
createChild(child, currentDepth)
}
parent.children.push(child);
}
}
createChild(data, 0);
Updated Vue.JS click code
function() {
if (this.isFolder) {
this.open = !this.open
}else{
var firstSiblingWithChildren;
// cycle through current node's parent's children (ie. siblings) and return the name of the first node that has children
for(var x = 0, len = this.model.parent.children.length; x < len; x++){
if(this.model.parent.children[x].hasOwnProperty('children') && Array.isArray(this.model.parent.children[x].children)){
firstSiblingWithChildren = this.model.parent.children[x].name;
break;
}
}
console.log(firstSiblingWithChildren);
}
},
I have an array of items in form of service - as I know it's like proper way to keep data you use in different controllers.I watched tutorial and did the filtering by brands. But I have an obstacle with filtering of my Items by another parameters, such as price, length and frequency... So I have made sliders by another example but I have no idea how to tie up it with my array of Items. Please do me a favour, show how to tie up even one of my parameters, price for example.
http://angular.zxcvbn.ru
services.js:
myApp.service('ProductDataService', function() {
//sample data
var items = [
{ name:'Picachoo', id:1, price:25000, pict:'http://www.metrord.do/_internal/gxml!0/2qijkhn0ctpwx8acoz5fxkpvtmr4nbh$r05jcw5nnz5dt1u7odn7q01jm5k3ezo/screen-shot-2016-07-24-at-11-55-41-am.jpeg', len: 250, friq: 5000, brand: 'came' },
{ name:'Tortule', id:2, price:30000, pict:'https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcR2APj6_uBfhHRXLn1dZN58ZocpzMxGMFLZmuqHEU5SybKN4QAVfg', len: 250, friq: 300, brand: 'came' },
{ name:'Dragon', id:3, price:33500, pict:'https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcSJotIxkjgXgm9m3m-0FuUxN6g9fGGXmP84VDRrPZoWa-x8Dqqd', len: 350, friq: 300, brand: 'came' },
{ name:'encrypted1', id:4, price:45000, pict:'http://gaidi.ru/wp-content/uploads/2016/07/kak-pravilno-lovit-pokemonov-v-pokemon-go.jpg', len: 400, friq: 3000, brand: 'came' },
{ name:'pravilno', id:5, price:48600, pict:'http://vignette3.wikia.nocookie.net/pokemon/images/2/2e/009Blastoise_Dream.png/revision/latest?cb=20140812050618', len: 550, friq: 2000, brand: 'came' },
{ name:'images', id:6, price:30000, pict:'https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcSqywi0lMtmf1sAR-20rg0HXETMueY3H71iJP35WsdsPHGVokK41g', len: 550, friq: 1000, brand: 'bft' },
{ name:'Foxy', id:7, price:38000, pict:'http://vgtimes.ru/uploads/posts/2016-07/1468938437_pk_vulpix.png', len: 350, friq: 10000, brand: 'bft' },
{ name:'Pteradactys', id:8, price:43000, pict:'https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcRJBXypwhl8-z4IsAZokgQlqPx_vZymtENBdlPy1HhN34uODEZ5', len: 800, friq: 10000, brand: 'bft' },
{ name:'encrypted', id:9, price:35800, pict:'https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcQZNKoA9LMtQHhgU4Toy7xXfzGEp6Rb4Kv6I16RgMjWO0Dnb36EFA', len: 1200, friq: 3000, brand: 'faac' },
{ name:'Jidjfj', id:10, price:14000, pict:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRPBKrRLvhYm9y-LxwRM4Qc_psMjd_luij_04ChmmQjcrdxgmcG3w', len: 800, friq: 5000, brand: 'fors' }
];
brands = {came : true, bft : true, faac : true, fors : true} ;
this.getItems = function() {
return items;
};
this.getBrands = function() {
return brands;
};
this.maxPrice = function() {
var max;
var producto;
for(var i = 0; i < items.length-1; i++) {
for(var j = 0; j < items.length-i-1; j++){
if (producto[j] > producto[j+1]) {
max = producto[j];
producto[j] = producto[j+1];
producto[j+1] = b;
}
}
}
return max;
}
this.minPrice = function() {
var min;
var producto;
for(var i = 0; i < items.length-1; i++) {
for(var j = 0; j < items.length-i-1; j++){
if (producto[j] < producto[j+1]) {
min = producto[j];
producto[j] = producto[j+1];
producto[j+1] = b;
}
}
}
return min;
}
});
slider_price.js:
myApp.controller('priceCtrl', function($scope, ProductDataService) {
$scope.search = { price_min : '', price_max : '', amount_min : 14000, amount_max : 48600 };
});
/* Range Slider
Input with default values:
-min=0 // Min slider value
-max=100 // Max slider value
-step=1 // Steps
Output / Input model
-value-min // Default value #min
-value-max // Default value #max
example:
<slider-range min="0" max="100" step="5" value-min="scope.form.slider_value_min" value-max="scope.form.slider_value_max"></slider-range>
*/
myApp.directive('priceRange', ['$document',function($document) {
// Move slider handle and range line
var moveHandle = function(handle, elem, posX) {
$(elem).find('.handle.'+handle).css("left",posX +'%');
};
var moveRange = function(elem,posMin,posMax) {
$(elem).find('.range').css("left",posMin +'%');
$(elem).find('.range').css("width",posMax - posMin +'%');
};
return {
template: '<div class="slider horizontal">'+
'<div class="range"></div>'+
'<a class="handle min" ng-mousedown="mouseDownMin($event)"></a>'+
'<a class="handle max" ng-mousedown="mouseDownMax($event)"></a>'+
'</div>',
replace: true,
restrict: 'E',
scope:{
valueMin:"=",
valueMax:"="
},
link: function postLink(scope, element, attrs) {
// Initilization
var dragging = false;
var startPointXMin = 0;
var startPointXMax = 0;
var xPosMin = 0;
var xPosMax = 0;
var settings = {
"min" : (typeof(attrs.min) !== "undefined" ? parseInt(attrs.min,10) : 0),
"max" : (typeof(attrs.max) !== "undefined" ? parseInt(attrs.max,10) : 100),
"step" : (typeof(attrs.step) !== "undefined" ? parseInt(attrs.step,10) : 1)
};
if ( typeof(scope.valueMin) == "undefined" || scope.valueMin === '' )
scope.valueMin = settings.min;
if ( typeof(scope.valueMax) == "undefined" || scope.valueMax === '' )
scope.valueMax = settings.max;
// Track changes only from the outside of the directive
scope.$watch('valueMin', function() {
if (dragging) return;
xPosMin = ( scope.valueMin - settings.min ) / (settings.max - settings.min ) * 100;
if(xPosMin < 0) {
xPosMin = 0;
} else if(xPosMin > 100) {
xPosMin = 100;
}
moveHandle("min",element,xPosMin);
moveRange(element,xPosMin,xPosMax);
});
scope.$watch('valueMax', function() {
if (dragging) return;
xPosMax = ( scope.valueMax - settings.min ) / (settings.max - settings.min ) * 100;
if(xPosMax < 0) {
xPosMax = 0;
} else if(xPosMax > 100) {
xPosMax = 100;
}
moveHandle("max",element,xPosMax);
moveRange(element,xPosMin,xPosMax);
});
// Real action control is here
scope.mouseDownMin = function($event) {
dragging = true;
startPointXMin = $event.pageX;
// Bind to full document, to make move easiery (not to lose focus on y axis)
$document.on('mousemove', function($event) {
if(!dragging) return;
//Calculate handle position
var moveDelta = $event.pageX - startPointXMin;
xPosMin = xPosMin + ( (moveDelta / element.outerWidth()) * 100 );
if(xPosMin < 0) {
xPosMin = 0;
} else if(xPosMin > xPosMax) {
xPosMin = xPosMax;
} else {
// Prevent generating "lag" if moving outside window
startPointXMin = $event.pageX;
}
scope.valueMin = Math.round((((settings.max - settings.min ) * (xPosMin / 100))+settings.min)/settings.step ) * settings.step;
scope.$apply();
// Move the Handle
moveHandle("min", element,xPosMin);
moveRange(element,xPosMin,xPosMax);
});
$document.mouseup(function(){
dragging = false;
$document.unbind('mousemove');
$document.unbind('mousemove');
});
};
scope.mouseDownMax = function($event) {
dragging = true;
startPointXMax = $event.pageX;
// Bind to full document, to make move easiery (not to lose focus on y axis)
$document.on('mousemove', function($event) {
if(!dragging) return;
//Calculate handle position
var moveDelta = $event.pageX - startPointXMax;
xPosMax = xPosMax + ( (moveDelta / element.outerWidth()) * 100 );
if(xPosMax > 100) {
xPosMax = 100;
} else if(xPosMax < xPosMin) {
xPosMax = xPosMin;
} else {
// Prevent generating "lag" if moving outside window
startPointXMax = $event.pageX;
}
scope.valueMax = Math.round((((settings.max - settings.min ) * (xPosMax / 100))+settings.min)/settings.step ) * settings.step;
scope.$apply();
// Move the Handle
moveHandle("max", element,xPosMax);
moveRange(element,xPosMin,xPosMax);
});
$document.mouseup(function(){
dragging = false;
$document.unbind('mousemove');
$document.unbind('mousemove');
});
};
}
};
}]);
app.js:
var myApp = angular.module("filterApp", []);
myApp.controller('ExampleController', ['$scope', '$window', function($scope, $window) {
$scope.greeting = 'Hello, World!';
$scope.doGreeting = function(greeting) {
$window.alert(greeting);
};
}]);
myApp.controller('coolCtrl', function($scope) {
$scope.whoAmI = function(){
console.log("This is whoAmI function in the coolCtrl!");
};
});
myApp.controller('mainCtrl', function($scope, ProductDataService){
$scope.helloWorld = function(){
console.log("This is helloWorld function in the mailCtrl!");
};
$scope.items = ProductDataService.getItems();
$scope.brands = ProductDataService.getBrands();
$scope.refresh = function(){
location.reload();
};
$scope.showPopUpMsg = false;
$scope.openPopUp = function( text ) {
$scope.showPopUpMsg = true;
$scope.popUpMsgContent = text;
$scope.minimun = 123;
};
});
jilters.js:
myApp.filter('brandsfilter', function () {
return function(input, filter) {
var result = [];
angular.forEach(input, function (item) {
angular.forEach(filter, function (isfiltered, brand) {
if (isfiltered && brand === item.brand) {
result.push(item);
}
});
});
return result;
};
});
index.php:
<?php
require 'scripts/mail_script.php';
?>
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" href="apple-touch-icon.png">
<!-- Place favicon.ico in the root directory -->
<link rel="stylesheet" href="styles/normalize.css">
<link rel="stylesheet" href="styles/main.css">
<link rel="stylesheet" href="styles/bootstrap.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <!-- for slider work -->
</head>
<body ng-app="filterApp" ng-controller='mainCtrl'>
<!--[if lt IE 8]>
<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please upgrade your browser to improve your experience.</p>
<![endif]-->
<!-- Add your site or application content here -->
<div class="container">
<section class='top' >
<h1>Filter</h1>
<!-- //// http://embed.plnkr.co/woqn3i/ /// -->
<!-- ///////// -->
<div class="row" id='items'>
<div>
<div class="col-md-4" ><h2>params:</h2>
<div ng-controller="priceCtrl">
<p>Choose price:</p>
<p>min: <input type="number" ng-model="search.amount_min"/> $</p>
<p>max: <input type="number" ng-model="search.amount_max"/> $</p>
<slider-range min="14000" max="48600" step="100" value-min="search.amount_min" value-max="search.amount_max"></slider-range>
</div>
<hr>
<p>По названию:</p>
<p><input type="text" ng-model="price" /></p>
<p>Brand:</p>
<ul >
<li ng-repeat="(brand, value) in brands">
<input type="checkbox" ng-model="brands[brand]" /> {{brand}}
</li>
</ul>
<hr>
<!-- //// http://embed.plnkr.co/woqn3i/ /// -->
<div ng-controller="filterCtrl">
<p>length:</p>
<p>От: <input type="text" ng-model="search.price_min"/> meters</p>
<p>До: <input type="text" ng-model="search.price_max"/> meters</p>
<slider-range min="2.5" max="12" value-min="search.price_min" value-max="search.price_max"></slider-range>
<hr>
<p>friquincy:</p>
<p>Min: <input type="number" ng-model="search.amount_min"/> times</p>
<p>Max: <input type="number" ng-model="search.amount_max"/> times</p>
<slider-range min="300" max="10000" step="100" value-min="search.amount_min" value-max="search.amount_max"></slider-range>
</div>
<hr>
<!-- ///////// -->
<div class="actions"/>
<input type="button" value="Сбросить" ng-click='refresh()'>
</div>
</div>
<div class="col-md-8"><h2>______ kinds</h2>
<?php
if($success) echo '<h4 class="bg-success text-center">'.'Сообщение отправлено!' . '</h4>';
if($error_message) echo '<h4 class="bg-danger text-center">'.'Сообщение не отправлено:' . $error_message. '</h4>';
?>
<div class="row">
<!-- LOOP.-->
<div ng-repeat="item in items | filter:price | orderBy:price | brandsfilter:brands ">
<div class="col-md-4 col-sm-6 col-sx-12">
<div class="item-inner">
<div class="image-product">
<div class="caption">
<div class="box-view">
<div class="name">{{item.name}}</div>
</div>
</div>
<img src="{{item.pict}}" alt="{{item.name}}" title="{{item.name}}" class="img-responsive">
<p class="price"><span class="price">Price: {{item.price}} $.</span></p>
<p class="description">{{item.name}} - just another piece of information, first preporty is {{item.len}} m. <br>another is: {{item.friq}} per day. </p>
</div>
</div>
</div><!-- col -->
</div> <!-- ng-repeat -->
</div> <!-- row -->
</div>
</div> <!-- mainCtrl -->
</section>
</div>
<div class="panel-footer">
<script src="vendor/angular.js"></script>
<script src="scripts/app.js"></script>
<script src="scripts/services.js"></script>
<script src="scripts/filters.js"></script>
<script src="scripts/slider.js"></script>
<script src="scripts/slider_price.js"></script>
<script src="vendor/jquery.js"></script>
<script src="vendor/bootstrap.js"></script>
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
</div>
</body>
</html>
AngularJS built-in filter which you already used supports multiple keywords filter see, you only need to implement comparator function to fulfill your needs.
First, change your filter in template to
<div ng-repeat="item in items | filter:myFilters:myComparator | orderBy:price">
then inside your filterCtrl you need to define myFilters and myComparator
$scope.myFilters = {price: 4000, brand: 'abc'};
$scope.myComparator = function(actual, expected) {
if (actual == parseInt(actual)) {
return actual > expected;
}
else {
return actual == expected;
}
}
Inside myComparator you can customise how you want to filter it (exact match, arithmetic comparison or partial match, just remember to return Boolean.
Just update $scope.myFilters whenever your filter keywords changed, the result will be updated in next digest cycle.
Using knockout, I have a select (a list of names) whose options are bound to another set of knockout-bound data (people). When the name of any person changes, the value of the select option that is bound to that person's name is correctly updated. However, the select's selection is not preserved if you had that person selected already.
See this jsFiddle for a live example: http://jsfiddle.net/DbBZQ/
Select "Jane" from the list.
Change the name "Jane" to something else ("Jane Doe" for example).
Notice the select defaults back to the first item.
How can I make the selection stick to the same option index even if the underlying value has changed? Is there a way to instruct knockout to preserve the selection or do I have to do this separately using JS?
Complete Code Example
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
var data =
{
people: ko.observableArray(
[
{ name: ko.observable("Jim") },
{ name: ko.observable("Jane") },
{
name: ko.observable("Sam"),
subordinates: ko.observableArray(
[
{
name: ko.observable("Tambone"),
subordinates: ko.observableArray(
[
{ name: ko.observable("Edward") },
{ name: ko.observable("Kristy") },
{ name: ko.observable("Thomas") },
{ name: ko.observable("Andy") }
])
},
{ name: ko.observable("Jules") }
])
}
])
};
var allNames = ko.computed(function ()
{
var names = [];
var selector = function (name, indent)
{
var option =
{
value: name,
text: (indent || "") + name
};
return option;
};
for (var i = 0; i < data.people().length; i++)
{
names.push(selector(data.people()[i].name()));
addSubordinates(names, 1, data.people()[i].subordinates, selector);
}
return names;
});
function addSubordinates(names, depth, subordinates, selector)
{
if (subordinates != null)
{
var indentText = "";
for (var i = 0; i < depth; i++)
indentText += ". . ";
for (var i = 0; i < subordinates().length; i++)
{
names.push(selector(subordinates()[i].name(), indentText));
addSubordinates(names, depth + 1, subordinates()[i].subordinates, selector);
}
}
}
</script>
</head>
<body>
<div data-bind="foreach: data.people">
<input type="text" data-bind="value: name" /><br />
</div>
Add Person
<br /><br /><br />
<select data-bind="options: allNames, optionsValue: 'value', optionsText: 'text', optionsCaption: 'All Names...'" />
<script type="text/javascript">
ko.applyBindings();
</script>
</body>
</html>
The reason the selection is lost is because the selected value is matched directly to the name property, which changes. As a result, the selected value no longer exists in the data source (allNames).
If you want to retain the selection, you have a couple of options:
Implement a hack such as tracking the index, and resetting it after the value changes
Bind the selected value to a property that doesn't change.
Do you have an immutable property that you can use as the selected value?
For the sake of an example, I added an id property to the objects in the data source, and use that as the selected value instead of name. This works the way you expect:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-2.2.1.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
var data =
{
people: ko.observableArray(
[
{ id: 1, name: ko.observable("Jim") },
{ id: 2, name: ko.observable("Jane") },
{
id: 3, name: ko.observable("Sam"),
subordinates: ko.observableArray(
[
{
id: 4, name: ko.observable("Tambone"),
subordinates: ko.observableArray(
[
{ id: 5, name: ko.observable("Edward") },
{ id: 6, name: ko.observable("Kristy") },
{ id: 7, name: ko.observable("Thomas") },
{ id: 8, name: ko.observable("Andy") }
])
},
{ id: 9, name: ko.observable("Jules") }
])
}
])
};
var allNames = ko.computed(function ()
{
var names = [];
var selector = function (id, name, indent)
{
var option =
{
value: id,
text: (indent || "") + name
};
return option;
};
for (var i = 0; i < data.people().length; i++)
{
names.push(selector(data.people()[i].id, data.people()[i].name()));
addSubordinates(names, 1, data.people()[i].subordinates, selector);
}
return names;
});
function addSubordinates(names, depth, subordinates, selector)
{
if (subordinates != null)
{
var indentText = "";
for (var i = 0; i < depth; i++)
indentText += ". . ";
for (var i = 0; i < subordinates().length; i++)
{
names.push(selector(subordinates()[i].id,subordinates()[i].name(), indentText));
addSubordinates(names, depth + 1, subordinates()[i].subordinates, selector);
}
}
}
</script>
</head>
<body>
<div data-bind="foreach: data.people">
<input type="text" data-bind="value: name" /><br />
</div>
Add Person
<br /><br /><br />
<select data-bind="options: allNames, optionsValue: 'value', optionsText: 'text', optionsCaption: 'All Names...'" />
<script type="text/javascript">
ko.applyBindings();
</script>
</body>
</html>
Edit:
As an alternative, what if you set up the value property so that it was a ko.computed that returned the index of the item? Like this:
var allNames = ko.computed(function ()
{
var names = [];
var selector = function (item, name, indent)
{
var option =
{
value: ko.computed(function(){ return data.people().indexOf(item);}),
text: (indent || "") + name
};
return option;
};
for (var i = 0; i < data.people().length; i++)
{
names.push(selector(data.people()[i], data.people()[i].name()));
addSubordinates(names, 1, data.people()[i].subordinates, selector);
}
return names;
});
function addSubordinates(names, depth, subordinates, selector)
{
if (subordinates != null)
{
var indentText = "";
for (var i = 0; i < depth; i++)
indentText += ". . ";
for (var i = 0; i < subordinates().length; i++)
{
names.push(selector(subordinates()[i],subordinates()[i].name(), indentText));
addSubordinates(names, depth + 1, subordinates()[i].subordinates, selector);
}
}
}