Watching and limiting size of array in angularjs scope - angularjs

The premise of what I am trying to do is to have a simple Q&A system that shows only one question at a time. After each answer is given another question is presented until $scope.quiz is exhausted.
So far it correctly displays the questions and appends both the question and answer to an array $scope.answers.
I need to somehow know and limit when the questions are exhausted so I can prepare a results page. I was thinking that somehow this should be done in the scope rather than the template but am unsure how to watch the $scope.answers array in real time so I know when it hits it's last question, and from there how to then replace the question div with some results.
I would be grateful for any assistance.
Thank you
<div id="q" class="cta1_content ugh" ng-controller="testYourself">
<div ng-init="count=0">
<div ng-repeat="data in quiz" ng-show="data.id==count">
<h2 class="text-center">{{data.name}}</h2>
<div class="col-xs-6 col-md-3 col-md-offset-3 text-center yesno">
<a href="#dropdown" class="q" ng-click="$parent.count=$parent.count+1;addAnswer(count, 1)">
<span class="cta_next"><i class="icon ion-checkmark-round"></i></span>
</a>
</div>
<div class="col-xs-6 col-md-3 text-center yesno">
<a href="#dropdown" class="q" ng-click="$parent.count=$parent.count+1;addAnswer(data.id, 0)">
<span class="cta_next"><i class="icon ion-close-round"></i></span>
</a>
</div>
</div>
</div>
</div>
app.controller('testYourself', ['$scope', function($scope) {
$scope.quiz = [
{id:0, name:"q1", answer: [{0: 'a1', 1: 'a2'}], weight:25},
{id:1, name:"q2", answer: [{0: 'a1', 1: 'a2'}], weight:25},
];
$scope.answers = [];
$scope.addAnswer = function(q, a) {
$scope.answers.push({'question':q, 'answer':a});
};
}]);

I would simply replace your addAnswer question with the following :
$scope.switchToResults = false;
$scope.proceeed = function(q, a) {
$scope.answers.push({'question':q, 'answer':a});
if ($scope.answers.length === $scope.quiz.length) {
switchToResults = true;
// do some business logic to process results
}
}
And add ng-if="!switchToResults" to your quiz div, and ng-if="!switchToResults" to your result div.

Related

Angular. Why filter invokes automatically?

I'm new in angular and I reeding A.Freeman's book "Pro Angular JS".
So I stuck in one of examples trying to understand why filter in ng-repeat is triggered.
Here is the code:
<body ng-controller="sportsStoreCtrl">
<div class="navbar navbar-inverse">
<a class="navbar-brand" href="#">SPORTS STORE</a>
</div>
<div class="panel panel-default row" ng-controller="productListCtrl">
<div class="col-xs-3">
<a ng-click="selectCategory()" class="btn btn-block btn-default btn-lg">Home</a>
<a ng-repeat="item in data.products | orderBy:'category' | unique:'category'" ng-click="selectCategory(item)" class=" btn btn-block btn-default btn-lg">
{{item}}
</a>
</div>
<div class="col-xs-8">
<div class="well" ng-repeat="item in data.products | filter:categoryFilterFn">
<h3>
<strong>{{item.name}}</strong>
<span class="pull-right label label-primary">
{{item.price | currency}}
</span>
</h3>
<span class="lead">{{item.description}}</span>
</div>
</div>
</div>
</body>
and
angular.module("sportsStore")
.controller("productListCtrl", function ($scope, $filter) {
var selectedCategory = null;
$scope.selectCategory = function (newCategory) {
selectedCategory = newCategory;
}
$scope.categoryFilterFn = function (product) {
return selectedCategory == null ||
product.category == selectedCategory;
}
});
categoryFilterFn is one that confuses me. Why it's invoking when I press catefory buttons (with selectCategory() method on ng-click) since I never call categoryFilterFn explicitly?
Answering you question - because of $digest. You don't have call categoryFilterFn directly. Your selectedCategory has changed which is used in categoryFilterFn and categoryFilterFn is bound to scope.
Not sure how I can describe it correctly but here my explanation.
There are two "independent" parts :
The repeat iterate over an array of items.
If you select an category via ng-click function you set the new category in the scope.
Here kicks the filter function in, witch ties it up.
It is triggered because a new category is selected ($digest) and "reordering" the array (like map function in plain Javascript) and the angular magic (ng-repeat) displays only items with this category.
And that's the reason why I love angular so much 🤓

Why do I don't get no data back from the array?

I don't get the correct data back. What is my mistake?
var ref = firebase.database().ref("Entries/kategorie");
ref.once("value")
.then(function(snapshot) {
$scope.tmp = snapshot.val();
$scope.$apply(function () {
$scope.data = $scope.tmp
// returns what you see in the image
console.log($scope.tmp.catname);
// The Log statement returns ["Cat1", "Cat2", "Cat3"] so the array which i need.
})
});
This is what i get back.
This is the html
<div ng-repeat="cat in data" class="animated lightSpeedIn">
<a nav-transition="none"><div ng-style="{'background': 'url(' + cat.bgurl + ')','background-repeat': 'no-repeat','background-size': 'cover','display': 'block','width': '100%','height': '25vh' }" class="bgcat center">
<div class="inner">
<h1>{{cat.catname}}</h1>
<h4>{{cat.subtitle}}</h4>
</div>
</div></a>
</div>
I thought with cat.catname it's possible to get the catnameurl. But that seems to be wrong...
Your returned data is in little bit incorrect format. The correct way is to return array of objects
But, you can do this:
<div ng-repeat="catname in data.catname" class="animated lightSpeedIn">
<a nav-transition="none"><div ng-style="{'background': 'url(' + data.bgurl[$index] + ')','background-repeat': 'no-repeat','background-size': 'cover','display': 'block','width': '100%','height': '25vh' }" class="bgcat center">
<div class="inner">
<h1>{{catname}}</h1>
<h4>{{cat.subtitle[$index]}}</h4>
</div>
</div></a>
</div>
You do have a very "unusual" data model. But I leave that up to you. Based on your data model, your html should look like this:
<div ng-repeat="bgurl in data.bgurl" class="animated lightSpeedIn">
<a nav-transition="none"><div ng-style="{'background': 'url(' + bgurl + ')','background-repeat': 'no-repeat','background-size': 'cover','display': 'block','width': '100%','height': '25vh' }" class="bgcat center">
<div class="inner">
<h1>{{data.catname[$index]}}</h1>
<h4>{{data.subtitle[$index]}}</h4>
</div>
I would recommend that you structure your data like this (attention: this structure does not fit the HTML that I just proposed in this answer)
[
{bgurl:'abc', catname:'name1', subtitle:'my first cat'},
{bgurl:'xyz', catname:'name2', subtitle:'my second cat'}
]

Repeating the nested objects of a single element with angular

Most of the answers I've found are for repeating the entire object, and that works for me without problem. What I need is to display the objects of an element in an array. Here is my controller:
var app = angular.module('QLoad', []);
app.controller('loadCtrl', function($scope) {
$scope.question=[
{Id:'1', qText:'What\'s your Gender?',aCollection:[
{text:'Male',
icon:'fa fa-male fa-5x'},
{text:'Female',
icon:"fa fa-female fa-5x"}
]
},
{Id:'2', qText:'What kind of Pain do you have?',aCollection:[
{text:'Burning',
icon:'fa fa-fire fa-5x'},
{text:'Stinging',
icon:"fa-bee.png"}
]
}];
});
As I mentioned, I could easily do two nested ng-repeats and display everything, but I only need to display the answers for each individual question. That's why I have the following two views I've tried, but it neither works:
<h1>{{question[0].qText}}</h1>
<div ng-repeat="q in question">
<div ng-repeat="answer in q.aCollection" class="content">
<div class=button>
<i class="{{q[0].answer.icon}}" aria-hidden="true"></i>
{{q[0].answer.text}}
</div>
</div></div>
Nor does this work:
<h1>{{question[0].qText}}</h1>
<div ng-repeat="answer in question[0].aCollection" class="content">
<div class=button>
<i class="{{answer.icon}}" aria-hidden="true"></i>
{{answer.text}}
</div>
</div></div>
And this is where I'm stuck. I can't display a single question object and its nested array. Thank you for your help.
You can use [limitTo][1] filter to achieve that.
<div ng-repeat="q in question | limitTo:1">
<div ng-repeat="answer in q.aCollection" class="content">
<div class=button>
<i class="{{answer.icon}}" aria-hidden="true"></i>
{{answer.text}}
</div>
</div></div>
You can define an index which will be incremented with the ng-click.
Template
<h1>{{question[questionTrackIndex].qText}}</h1>
<div ng-repeat="answer in question[questionTrackIndex].aCollection" class="content">
<div class=button>
<i class="{{answer.icon}}" aria-hidden="true"></i>
{{answer.text}}
</div>
</div>
<div class="btn btn-warning" ng-if="questionTrackIndex != 0" ng-click="previousQuestion()">Previous</div>
<div class="btn btn-warning" ng-if="question.length - 1 > questionTrackIndex" ng-click="nextQuestion()">Next</div>
Controller
$scope.questionTrackIndex = 0;
$scope.question=[
{Id:'1', qText:'What\'s your Gender?',aCollection:[
{text:'Male',
icon:'fa fa-male fa-5x'},
{text:'Female',
icon:"fa fa-female fa-5x"}
]
},
{Id:'2', qText:'What kind of Pain do you have?',aCollection:[
{text:'Burning',
icon:'fa fa-fire fa-5x'},
{text:'Stinging',
icon:"fa-bee.png"}
]
},
{Id:'3', qText:'Question no 3',aCollection:[
{text:'Answer1',
icon:'fa fa-fire fa-5x'},
{text:'Answer2',
icon:"fa-bee.png"}
]
}
];
$scope.nextQuestion = function() {
$scope.questionTrackIndex++;
}
$scope.previousQuestion = function() {
$scope.questionTrackIndex--;
}
The variable $scope.questionTrackIndex has been used to scroll through the questions. I have added two buttons next and previous to scroll through the questions. With clicking next button, $scope.questionTrackIndex has been incremented and with clicking previous button, $scope.questionTrackIndex has been decremented.
See this PLUNKER
One of solution maybe is
<div ng-repeat="q in question">
<div ng-click="a($index)">{{q.qText}}</div>
</div>
<h1>{{answ.text}}</h1>
<div ng-repeat="answer in answ.answerList" class="content">
<div class=button>
<i class="{{answer.icon}}" aria-hidden="true"></i>
{{answer.text}}
</div>
and your controller look like
$scope.answ = {};
$scope.answ.text = $scope.question[0].qText
$scope.answ.answerList = $scope.question[0].aCollection;
$scope.a = function(index){
$scope.answ.text = $scope.question[index].qText
$scope.answ.answerList = $scope.question[index].aCollection;
}
Example you can see on link

Angularjs - how to shuffle the class names using the scope function?

I have 3 elements, which shows by conditional. on click of them using the 'ng-class' I am showing or hiding the element.
here is the HTML :
<div class="userOptions">
<div class="row designOption" ng-class="{'active':proDesign}">
<span ng-click="categories('proDesign')" class="icon iconD">D</span>
<span class="info infoD">Design</span>
</div>
<div class="row suplyOption" ng-class="{'active':Supply}">
<span ng-click="categories('proSupply')" class="icon iconS">S</span>
<span class="info infoS">Supply</span>
</div>
<div class="row constOption" ng-class="{'active':Construct}">
<span ng-click="categories('proConstruct')" class="icon iconC">C</span>
<span class="info infoC">Construction</span>
</div>
</div>
But using the scope function I am not able to shuffle the class name active. here is my try:
$scope.proDesign = false;
$scope.proConstruct = false;
$scope.proSupply = false;
$scope.categories = function (categ) {
$scope.proDesign = false; //all are false.
$scope.proConstruct = false;
$scope.proSupply = false;
$scope[categ] = !$scope[categ]; //became true
}
But this is not working. if so, what is the correct way to do this?

AngularJS Duplicate key array in a repeater

Hi currently i´m programming a application in AngularJs with api rest, but there is something that i miss.
Customers can create organizations with their branchoffices. To create and show the organizations and branchoffices i use the code below.
when the user create an organization there isn´t any problem, but when the user create the second one, the code update the first entry and generate as second one with the same value. so you hava a duplicate key.
What i´m doing wrong?
below you can see the code and pictures attached
First time introduce data and send api, but second time appear the same text. I introduce the new value and send to api rest, and api answer with a right values, but myorganizations variable has the duplicate key with the new value.
First time
Second time
Results
HTML File
<div class="row">
<h4>Mis Organizaciones <small><span class="glyphicon glyphicon-plus"></span></small></h4>
</div>
<div class="row" ng-repeat="organization in myorganizations.data">
<h5>{{organization.company_name}}</h5>
<div ng-repeat="branch in organization.branches">
<div class="col-lg-3 col-md-4 col-sm-6 col-xs-12 ">
<div class="thumbnail">
<a ui-sref="mybranchoffice({name: '{{branch.branch_name}}', id: '{{branch.branch_id}}', company_id: '{{organization.company_id}}'})">
<div class="thumbnail-organization" style="background: url({{branch.branch_image_url}}) center center no-repeat;"></div>
</a>
<div class="caption">
<h6 class="thumbnail-organization-name"></h6>
</div>
</div>
</div>
<div class="clearfix visible-sm" ng-if="($index+1)%2==0"></div>
<div class="clearfix visible-md" ng-if="($index+1)%3==0"></div>
<div class="clearfix visible-lg" ng-if="($index+1)%4==0"></div>
</div>
<div class="col-lg-5 col-md-5 col-sm-6 col-xs-12">
<div class="jumbotron">
<a type="button" href="" data-company="{{organization.company_id}}" data-toggle="modal" data-target="#branchModal">Create new Branch</a>
</div>
</div>
</div>
<div class="row col-lg-5 col-md-5 col-sm-6 col-xs-12">
<div class="jumbotron">
<a type="button" href="" data-toggle="modal" data-target="#organizationModal" >Create new Organization</a>
</div>
</div>
</div>
Controller
$scope.newOrganization = function () {
butlerService.createOrganization($scope.organization).then(function(neworganization){
console.log(neworganization);
tmp.data[0].company_id = neworganization.data.company_id;
tmp.data[0].company_name = neworganization.data.company_name;
tmp.data[0].branches=[];
console.log(tmp);
$scope.myorganizations.data.push(tmp.data[0]);
console.log($scope.myorganizations);
}), function (error){
}
}
Service
createOrganization: function (organization) {
var deferred = $q.defer();
$http({
method: "post",
url: urlCreateOrganization,
data: organization
})
.success(function(response){
deferred.resolve(response);
})
.error(function(err){
deferred.reject(err);
});
return deferred.promise;
}
Without seeing your variable init it is hard to say for sure, but it seems that here:
tmp.data[0].company_id = neworganization.data.company_id;
tmp.data[0].company_name = neworganization.data.company_name;
tmp.data[0].branches=[];
you are rewriting over the first element in your array. And here:
$scope.myorganizations.data.push(tmp.data[0]);
You are pushing once more the same reference to variable in an array.
Just create new variable before you assign values to it and before you push it in the array.
Something like this:
var tmp = {};
tmp.company_id = neworganization.data.company_id;
tmp.company_name = neworganization.data.company_name;
tmp.branches=[];
console.log(tmp);
$scope.myorganizations.data.push(tmp);
FYI http returns promisse, you don't have to wrap it once more.

Resources