This error happens when I try to upload two files simultaneously that call the same controller for submission. Here is the relevant code:
<ion-list id="licenseDetailsInReview-list4" class=" ">
<ion-item class="item-divider " id="licenseDetails-list-item-divider5">Image 1
<div ng-if="doc.status!='approved'" class="edit">
<div class="button button-clear button-small has-file button-black " type="button">
<span style="color:black" class="image1"><i class="icon ion-edit icon-accessory custom"></i></span>
<input type="file" accept="image/*;capture=camera" name="{{doctype.doctype.documentType}}" onchange="angular.element(this).scope().$parent.uplCtrl.fileChanged(this.files, this,1)">
</div>
</div>
</ion-item>
<div class="thumbimg">
<span class="imgdet1"><div ng-if ="doc.thumb1"><img src="{{doc.thumb1.url}}"></div>
<div ng-if="!doc.thumb1"><i class="icon ion-image" style="font-size: 64px; color: rgb(136, 136, 136); vertical-align: middle;"></i></div>
</div>
</ion-list>
<ion-list id="licenseDetailsInReview-list4" class=" ">
<ion-item class="item-divider " id="licenseDetails-list-item-divider5">Image 2
<div ng-if="doc.status!='approved'" class="edit">
<div class="button button-clear button-small has-file button-black edit" type="button">
<span style="color:black" class="image2"><i class="icon ion-edit icon-accessory custom"></i></span>
<input type="file" accept="image/*;capture=camera" name="{{doctype.doctype.documentType}}" onchange="angular.element(this).scope().$parent.uplCtrl.fileChanged(this.files, this,2)">
</div>
</div>
</ion-item>
<div class="thumbimg">
<span class="imgdet2"><div ng-if ="doc.thumb2"><img src="{{doc.thumb2.url}}"></div>
<div ng-if="!doc.thumb2"><i class="icon ion-image" style="font-size: 64px; color: rgb(136, 136, 136); vertical-align: middle;"></i></div>
</div>
</ion-list>
And the controller methods :
fileChanged(files, type, i) {
const self = this;
const file = files[0];
console.log(type.name, files);
window.URL = window.URL || window.webkitURL;
self['files'] = self['files'] || {};
self['files'][type.name] = {
file: file,
blob: window.URL.createObjectURL(file)
};
var typeHolder = document.querySelector('.image' + i).innerHTML = "Uploading File.." + file.name;
this.$scope.$apply();
this.submit(self.$scope.user, i)
}
And the submit method:
submit(user, i) {
console.log('user', user);
const self = this;
//var UserDocument = Parse.Object.extend('UserDocument');
this.$scope.mutex =0;
var promises = [];
for (self.$scope.doctype.documentType in this.files) {
console.log("Files", this.files)
if (this.files.hasOwnProperty(self.$scope.doctype.documentType)) {
var parseFile = new Parse.File(self.$scope.doctype.documentType + i +'.' + this.files[self.$scope.doctype.documentType].file.name.split('.').pop(), this.files[self.$scope.doctype.documentType].file);
if (!self.$scope.doc) {
var objUserDocType = new this.UserDocumentType();
console.log("reached here");
var docType = self.$scope.usd.find(o => o.documentType == self.$scope.doctype.documentType);
objUserDocType.id = docType.objectId;
this.objUserDoc.set('docType', objUserDocType);
}
// console.log("reached here too!");
this.objUserDoc.set('owner', Parse.User.current());
//objUserDoc.set('status', 'inReview');
this.objUserDoc.set('image' + i, parseFile);
self.$scope.submitted = 1;
var p = this.objUserDoc.save().....
So when I try to upload one image before the other one has been saved, I get the error:
upload.controller.js:110 error objUserDoc Error: Tried to encode an unsaved file.
at encode (http://localhost:3000/app-1db45d.js:92569:14)
at ParseObjectSubclass._getSaveJSON (http://localhost:3000/app-1db45d.js:88168:40)
at ParseObjectSubclass._getSaveParams (http://localhost:3000/app-1db45d.js:88176:24)
at task (http://localhost:3000/app-1db45d.js:89758:34)
at TaskQueue.enqueue (http://localhost:3000/app-1db45d.js:94528:10)
at Object.save (http://localhost:3000/app-1db45d.js:89768:31)
at ParsePromise.wrappedResolvedCallback (http://localhost:3000/app-1db45d.js:90767:44)
at ParsePromise.resolve (http://localhost:3000/app-1db45d.js:90705:37)
at ParsePromise.<anonymous> (http://localhost:3000/app-1db45d.js:90777:30)
at ParsePromise.wrappedResolvedCallback (http://localhost:3000/app-1db45d.js:90767:44)
What can I do to resolve this??
thanks in advance
Two key ideas are needed: (a) we must save the file, then save the referencing object, and (b) to do N file-object saves, accumulate the save promises first, then execute all of them with Promise.all().
I tried to re-arrange your code to illustrate, but with an important caveat: I don't understand the app, nor can I see the controller surrounding all this, so the code here must be checked carefully to insure that it correctly refers to the controller level objects, including $scope.
// save a parse file, then the parse object that refers to it
function saveUserDocWithFile(objUserDoc, doctype) {
var parseFile = new Parse.File(doctype.documentType + i +'.' + this.files[doctype.documentType].file.name.split('.').pop(), this.files[doctype.documentType].file);
// first save the file, then update and save containing object
this.$scope.submitted = 1;
return parseFile.save().then(function() {
objUserDoc.set('image' + i, parseFile);
return objUserDoc.save();
});
}
With that, the submit function is simpler. It just needs to create an array of promises and execute them (using Promise.all()).
submit(user, i) {
console.log('user', user);
const self = this;
//var UserDocument = Parse.Object.extend('UserDocument');
self.$scope.mutex =0;
var promises = [];
for (self.$scope.doctype.documentType in self.files) {
console.log("Files", self.files)
if (self.files.hasOwnProperty(self.$scope.doctype.documentType)) {
if (!self.$scope.doc) {
self.objUserDoc.set('docType', objUserDocType());
}
self.objUserDoc.set('owner', Parse.User.current());
var promise = saveUserDocWithFile(self.objUserDoc, self.$scope.doctype);
promises.push(promise);
}
}
return Promise.all(promises);
}
// factor out objUserDocType creation for readability
function objUserDocType() {
var objUserDocType = new this.UserDocumentType();
var docType = this.$scope.usd.find(o => o.documentType == this.$scope.doctype.documentType);
objUserDocType.id = docType.objectId;
return objUserDocType;
}
Related
It is not like it is slow on rendering many entries. The problem is that whenever the $scope.data got updated, it adds the new item first at the end of the element, then reduce it as it match the new $scope.data.
For example:
<div class="list" ng-repeat="entry in data">
<h3>{{entry.title}}</h3>
</div>
This script is updating the $scope.data:
$scope.load = function() {
$scope.data = getDataFromDB();
}
Lets say I have 5 entries inside $scope.data. The entries are:
[
{
id: 1,
title: 1
},
{
id: 2,
title: 2
},
......
]
When the $scope.data already has those entries then got reloaded ($scope.data = getDataFromDB(); being called), the DOM element for about 0.1s - 0.2s has 10 elements (duplicate elements), then after 0.1s - 0.2s it is reduced to 5.
So the problem is that there is delay about 0.1s - 0.2s when updating the ng-repeat DOM. This looks really bad when I implement live search. Whenever it updates from the database, the ng-repeat DOM element got added up every time for a brief millisecond.
How can I make the rendering instant?
EDITED
I will paste all my code here:
The controller:
$scope.search = function (table) {
$scope.currentPage = 1;
$scope.endOfPage = false;
$scope.viewModels = [];
$scope.loadViewModels($scope.orderBy, table);
}
$scope.loadViewModels = function (orderBy, table, cb) {
if (!$scope.endOfPage) {
let searchKey = $scope.page.searchString;
let skip = ($scope.currentPage - 1) * $scope.itemsPerPage;
let searchClause = '';
if (searchKey && searchKey.length > 0) {
let searchArr = [];
$($scope.vmKeys).each((i, key) => {
searchArr.push(key + ` LIKE '%` + searchKey + `%'`);
});
searchClause = `WHERE ` + searchArr.join(' OR ');
}
let sc = `SELECT * FROM ` + table + ` ` + searchClause + ` ` + orderBy +
` LIMIT ` + skip + `, ` + $scope.itemsPerPage;
sqlite.query(sc, rows => {
$scope.$apply(function () {
var data = [];
let loadedCount = 0;
if (rows != null) {
$scope.currentPage += 1;
loadedCount = rows.length;
if (rows.length < $scope.itemsPerPage)
$scope.endOfPage = true
for (var i = 0; i < rows.length; i++) {
let item = rows.item(i);
let returnObject = {};
$($scope.vmKeys).each((i, key) => {
returnObject[key] = item[key];
});
data.push(returnObject);
}
$scope.viewModels = $scope.viewModels.concat(data);
}
else
$scope.endOfPage = true;
if (cb)
cb(loadedCount);
})
});
}
}
The view:
<div id="pageContent" class="root-page" ng-controller="noteController" ng-cloak>
<div class="row note-list" ng-if="showList">
<h3>Notes</h3>
<input ng-model="page.searchString" id="search"
ng-keyup="search('notes')" type="text" class="form-control"
placeholder="Search Notes" style="margin-bottom:10px">
<div class="col-12 note-list-item"
ng-repeat="data in viewModels track by data.id"
ng-click="edit(data.id)"
ontouchstart="touchStart()" ontouchend="touchEnd()"
ontouchmove="touchMove()">
<p ng-class="deleteMode ? 'note-list-title w-80' : 'note-list-title'"
ng-bind-html="data.title"></p>
<p ng-class="deleteMode ? 'note-list-date w-80' : 'note-list-date'">{{data.dateCreated | displayDate}}</p>
<div ng-if="deleteMode" class="note-list-delete ease-in" ng-click="delete($event, data.id)">
<span class="btn fa fa-trash"></span>
</div>
</div>
<div ng-if="!deleteMode" ng-click="new()" class="add-btn btn btn-primary ease-in">
<span class="fa fa-plus"></span>
</div>
</div>
<div ng-if="!showList" class="ease-in">
<div>
<div ng-click="back()" class="btn btn-primary"><span class="fa fa-arrow-left"></span></div>
<div ng-disabled="!isDataChanged" ng-click="save()" class="btn btn-primary" style="float:right">
<span class="fa fa-check"></span>
</div>
</div>
<div contenteditable="true" class="note-title"
ng-bind-html="selected.title" id="title">
</div>
<div contenteditable="true" class="note-container" ng-bind-html="selected.note" id="note"></div>
</div>
</div>
<script src="../js/pages/note.js"></script>
Calling it from:
$scope.loadViewModels($scope.orderBy, 'notes');
The sqlite query:
query: function (query, cb) {
db.transaction(function (tx) {
tx.executeSql(query, [], function (tx, res) {
return cb(res.rows, null);
});
}, function (error) {
return cb(null, error.message);
}, function () {
//console.log('query ok');
});
},
It is apache cordova framework, so it uses webview in Android emulator.
My Code Structure
<html ng-app="app" ng-controller="pageController">
<head>....</head>
<body>
....
<div id="pageContent" class="root-page" ng-controller="noteController" ng-cloak>
....
</div>
</body>
</html>
So there is controller inside controller. The parent is pageController and the child is noteController. Is a structure like this slowing the ng-repeat directives?
Btw using track by is not helping. There is still delay when rendering it. Also I can modify the entries as well, so when an entry was updated, it should be updated in the list as well.
NOTE
After thorough investigation there is something weird. Usually ng-repeat item has hash key in it. In my case ng-repeat items do not have it. Is it the cause of the problem?
One approach to improve performance is to use the track by clause in the ng-repeat expression:
<div class="list" ng-repeat="entry in data track by entry.id">
<h3>{{entry.title}}</h3>
</div>
From the Docs:
Best Practice: If you are working with objects that have a unique identifier property, you should track by this identifier instead of the object instance, e.g. item in items track by item.id. Should you reload your data later, ngRepeat will not have to rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones. For large collections, this significantly improves rendering performance.
For more information, see
AngularJS ngRepeat API Reference -- Tracking and Duplicates
In your html, try this:
<div class="list" ng-repeat="entry in data">
<h3 ng-bind="entry.title"></h3>
</div>
After thorough research, I found my problem. Every time I reset / reload my $scope.viewModels I always assign it to null / empty array first. This what causes the render delay.
Example:
$scope.search = function (table) {
$scope.currentPage = 1;
$scope.endOfPage = false;
$scope.viewModels = []; <------ THIS
$scope.loadViewModels($scope.orderBy, table);
}
So instead of assigning it to null / empty array, I just replace it with the new loaded data, and the flickering is gone.
I am new to JavaScript and kind of stuck at this place
function onView(data){
var item_size = data;
return item_size;
}
I have this function with parameter passing through HTML input, i want to use return of this function in another function
function onRegisterInput(){
var y= onView(data);
}
onRegisterInput is called onclick Button, i want to take return value of onView function as var y.
How can i do this ??
Everytime i click button onRegisterInput() function is called but my debugger shows data is undefined. Please help. Thanks in advance.
As you can see in onView, it takes data as function parameter, however you don't give it any parameter in your call var y= onView();. From my understanding, onView get's triggered when a button is clicked, so I'd suggest you save your value in a global variable so you can use it across functions
There are 2 ways for you to get the data in your button click callback.
Get data from the DOM
There are different ways to keep data inside the DOM, but data-* attributes are popular:
function testMe(event, button) {
const magicNumber = button.getAttribute('data-magic-number');
document.getElementById('result1').innerHTML = magicNumber;
}
function testMe2(event, button) {
const magicNumber = document.getElementById('magic2').getAttribute('data-magic-number');
document.getElementById('result2').innerHTML = magicNumber;
}
.test {
padding: 4px;
}
<div class="test">
<button id="button1" data-magic-number="123456789" onclick="testMe(event, this)">Button 123456789</button>
<div id="result1"></div>
</div>
<div class="test">
<span data-magic-number="987654321" id="magic2">Click this: </span><button id="button2" onclick="testMe2()">Button 987654321</button>
<div id="result2"></div>
</div>
Another option is to keep data inside the JS
let myMagicNumber = 1;
let resultDiv = document.getElementById('result');
function showNumber() {
resultDiv.innerText = myMagicNumber;
}
function incNumber() {
myMagicNumber++;
showNumber();
}
function decNumber() {
myMagicNumber--;
showNumber();
}
<div>
<button onclick="showNumber()">Show</button>
<button onclick="incNumber()">+1</button>
<button onclick="decNumber()">-1</button>
</div>
<div id="result">
</div>
Let's return to your example
function onView(data) {
// Some data processing
// We will return data length for example
return (typeof data === 'string' ? data.length : 0);
}
function onRegisterInput() {
// Button click
let data = document.getElementById('name').value;
if (data != '') {
var y = onView(data);
document.getElementById('result').innerText = 'Name: ' + data + ', length: ' + y;
} else {
document.getElementById('result').innerText = 'Enter your name';
}
}
<div>
<label>Name: <input type="text" id="name"/></label>
<button onclick="onRegisterInput()">Register</button>
</div>
<div id="result"></div>
so, I built a view with a ViewResult and the View has a text box to enter a search string and a button to submit. It works great, it return my searched items and displays on the screen. Now, my requirement is to move that text box to the home layout (below) and submit from there. But, when I press submit and it calls the ajax post, the debug shows the search string searching the viewResult and it even gets to the View associated, but the View does not render. any ideas?
Here is the the Search Box
<div style="float:right;">
<input type="text" id="searchString" name="searchString" />
<a href="#" id="search" ><img src="~/Images/Custom/searchIcon.jpg" style="width:25px; height:25px;"/></a>
</div>
Here is the javascript with the ajax post
<script>
$(document).ready(function () {
$("#search").click(function () {
var searchString = $("#searchString").val();
var datastring = { "searchString": searchString };
$.ajax({
url: "/Home/Search/",
type: "POST",
contentType: "application/json",
data: JSON.stringify(datastring),
datatype: 'json',
success: function () {
console.log('success!!');
}
});
});
});
</script>
and here is my ViewResult:
public ViewResult Search(string searchString, int? pageNumber)
{
// var searchString = fc["searchString"];
var results = new ArrayList();
var mylist = new List<SearchResult>();
var model = new SearchViewModel();
var host = "/CommunityWildlifeHabitat";
if (System.Web.HttpContext.Current.Request.IsLocal)
host = "";
// Search Communities
var search = from s in db.Communities
where s.ComunityName.Contains(searchString) || s.Description.Contains(searchString)
select s;
// Search Resource Center
var docs = from d in db.ResourceCenters
where d.Title.Contains(searchString) || d.Description.Contains(searchString)
select d;
// Set up Arraylist with type Community
foreach(var c in search)
{
var community = new SearchResult();
community.type = "community";
community.CommunityId = c.CommunityId;
community.CommunityName = c.ComunityName;
community.Description = c.Description;
community.CommunityType = c.CommunityType1.TypeName;
community.CommunityCity = c.CommunityCity;
community.CommunityState = c.CommunityState;
community.CommunityZip = c.CommunityZip;
community.Population = c.Population;
mylist.Add(community);
}
// Set up ArrayList with type ResourceCenter
foreach (var d in docs)
{
var document = new SearchResult();
document.type = "document";
document.Title = d.Title;
document.Document_Description = d.Description;
document.FilePath = d.FilePath;
document.Date = Convert.ToDateTime(d.Date);
document.UpLoadedBy = d.UpLoadedBy;
mylist.Add(document);
}
model.results = mylist;
ViewBag.results = model.results;
ViewBag.searchString = searchString;
ViewBag.Host = host;
return View(mylist.ToPagedList(pageNumber ?? 1, 10));
}
Lastly, here is my View:
<h2>Search</h2>
#using (Html.BeginForm("Search", "Home", FormMethod.Get, new { #class = "form-horizontal", role = "form" }))
{
#Html.TextBox("searchString", ViewBag.searchString as string, new { #class = "form-control", required = "required"})
<input type="submit" class="btn btn-default" value="Search" />
<hr />
if (#Model.Count != 0)
{
<h3>The following results were found for #ViewBag.searchString</h3>
foreach (var search in #Model)
{
if (#search.type == "community")
{
<div class="resource-element">
<a href="#ViewBag.Host/Communities/CommunityPage/#search.CommunityId">
<span class="resource-type pull-right">Community</span>
</a>
<h3>#search.CommunityName</h3>
<p>#search.Description</p>
<span class="">Type : #search.CommunityType</span><br />
<span class="">#search.CommunityCity, #search.CommunityState #search.CommunityZip</span><br />
<span class="">Population: #search.Population</span>
<br>
</div>
}
else
{
<div class="resource-element">
<a href="#ViewBag.Host#search.FilePath">
<span class="resource-type pull-right">Document</span>
</a>
<h3>#search.Title</h3>
<p>#search.Document_Description</p>
<span class="">#search.Date</span>
<br>
<span class="">#search.UpLoadedBy</span>
<br>
</div>
}
}
#Html.PagedListPager(Model, pageNumber => Url.Action("Search", "Home", new { searchString = #ViewBag.searchString, pageNumber }),
new PagedListRenderOptions() { Display = PagedListDisplayMode.IfNeeded, DisplayPageCountAndCurrentLocation = true })
}
else
{
if (#ViewBag.searchString != null)
{
<div class="resource-element">
<a href="#">
<span class="resource-type pull-right"></span>
</a>
<h3>No Results Found</h3>
</div>
}
}
}
if you simply change your form helper with the following
#using (Html.BeginForm("Search", "Home", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
and remove your jquery, it should post your form correctly to the server, and automatically reload the page.
There is no specific need in running an async call inside your page.
I'm using this example but with some modifications. I've added input methods to my app, so user can choose any json file from local pc and read it on a page then choose one more file compare it and see results on the bottom page.
But I'm getting every time error
document.getElementById(...).forEach is not a function
What am I doing wrong? Below my code.. and not working fiddle with the error:
app.controller("Main",function ($scope) {
// ===== Start FileReader =====
$scope.leftWindow = function readSingleLeftFile(e) {
var file = e.target.files[0];
if (!file) {
return;
}
var reader = new FileReader();
reader.onload = function (e) {
var leftcontent = e.target.result;
displayLeftContents(leftcontent);
};
reader.readAsText(file);
};
function displayLeftContents(leftcontent) {
$scope.leftElement = document.getElementById('left-content');
$scope.leftElement.innerHTML = leftcontent;
}
document.getElementById('left-input')
.addEventListener('change', $scope.leftWindow, false);
$scope.rightWindow = function readSingleRightFile(e) {
var file = e.target.files[0];
if (!file) {
return;
}
var reader = new FileReader();
reader.onload = function (e) {
var rightcontent = e.target.result;
displayRightContents(rightcontent)
};
reader.readAsText(file);
};
function displayRightContents(rightcontent) {
$scope.rightElement = document.getElementById('right-content');
$scope.rightElement.innerHTML = rightcontent;
}
document.getElementById('right-input')
.addEventListener('change', $scope.rightWindow, false);
// ===== End FileReader =====
$scope.results = (function(){
var leftInputIds = {};
var rightInputIds = {};
var result = [];
document.getElementById('left-input').forEach(function (el, i) {
leftInputIds[el.id] = document.getElementById('left-input')[i];
});
document.getElementById('right-input').forEach(function (el, i) {
rightInputIds[el.id] = document.getElementById('right-input')[i];
});
for (var i in rightInputIds) {
if (!leftInputIds.hasOwnProperty(i)) {
result.push(rightInputIds[i]);
}
}
return result;
}());
});
and div
<section ng-show="dnd">
<div class="content">
<div class="row">
<div class="childGrid" style="display: flex">
<div style="width: 50%">
<input type="file" id="left-input"/>
<h3>Contents of the file:</h3>
<pre id="left-content"></pre>
</div>
<div style="width: 50%">
<input type="file" id="right-input"/>
<h3>Contents of the file:</h3>
<pre id="right-content"></pre>
</div>
</div>
<div class="parentGrid">
<div id="compare">
{{results|json}}
</div>
</div>
</div>
</div>
</section>
document.getElementById() returns a single object and not an array.
forEach need an array in odrer to operate it.
forEach method is not defined for object but it is defined for array.
So,that's why you getting an error
document.getElementById(...).forEach is not a function
EDIT1 :
do like this :
var leftInput = document.getElementById('left-input')
leftInputIds[leftInput.id] = leftInput;
var rightInput = document.getElementById('right-input')
rightInputIds[rightInput.id] = rightInput;
I am creating a dynamic, single-paged forum site using AngularJS as the front-end and Firebase as the back-end. The page consists of a list of threads on the left-hand side and the thread content on the right-hand side. The thread content displayed is based on the thread selected from the list.
I can successfully select a thread from the list and display its contents. However, when a thread is selected from the list, all of the other threads in the list become replicas of the selected thread. By this, I mean that the attribute values for the title, comments and votes of the selected thread are assigned to the same attributes in all of the other threads simultaneously, making them all identical. The ID of each thread does not change.
Can anybody give me some insight as to what is causing this issue? I can't identify anything in my code that would cause the attribute values of each Firebase object to be reassigned.
Here is the main.html page that contains the list and thread content sections
<div ng-controller="mainPageController">
<div>
<h3>
Welcome {{user.name}}! <button class="btn-danger img-rounded" ng-click="logout()" id="LogoutBtn">Logout</button>
</h3>
</div>
<div class="col-md-6">
<h2>All Threads</h2>
<div id="searchThreads" class="input-group col-md-5 img-rounded">
<input type="text" class="col-xs-5 form-control" ng-model="searchThread" placeholder="Search threads...">
</div>
<div id="addThread" class="input-group">
<input type="text" class="col-xs-5 form-control" ng-model="newThreadTitle" placeholder="New thread title..."/>
<button ng-click="addThread()">Add thread</button>
</div>
<!-- Thread List -->
<div>
<div ng-repeat="thread in threads | filter:searchThread | orderObjectBy:'votes'">
<button class="glyphicon glyphicon-chevron-up" ng-click="upvote(thread.$id, thread.votes)"></button> |
<button class="glyphicon glyphicon-chevron-down" ng-click="downvote(thread.$id, thread.votes)"></button>
<a href ng-click="showThread(thread)">{{thread.votes}}<span style="margin-left:1em"> {{thread.title}} by {{thread.username}}</span></a>
</div>
</div>
</div>
</div>
<!-- Thread content viiew -->
<div class="col-md-6">
<div ng-controller="threadPageController">
<h1>{{currentThread.title}} by {{currentThread.username}}</h1>
<div>
<input type="text" ng-model="newComment" placeholder="Write a comment..."/>
<button ng-click="addComment()">Add Comment</button>
</div>
<div>
<div ng-repeat="comment in currentThread.comments">{{comment.username}}: {{comment.text}}
</div>
<div ng-if="!currentThread.comments.length">There are no comments on this thread</div>
</div>
</div>
</div>
The mainPageController
angular.module('richWebApp')
.controller('mainPageController', function($scope, $location, userService, threadService, fb, $firebaseAuth, $filter){
$scope.user = userService.getLoggedInUser();
$scope.newThreadTitle = '';
$scope.currentThreadId = '';
$scope.threads = threadService.getAllThreads();
$scope.threads.$loaded().then(function(){
console.log($scope.threads)
});
$scope.users = userService.getLoggedInUsers();
$scope.addThread = function(){
if(!$scope.newThreadTitle){
return false; //Don't do anything if the text box is empty
}
var newThread = {
title: $scope.newThreadTitle,
username: $scope.user.name,
comments: [],
votes: 0
};
$scope.threads.$add(newThread);
$scope.newThread = '';
$scope.newThreadTitle = ''; //Clear the text in the input box
}
$scope.showThread = function(thread) {
$scope.$emit('handleEmit', {id: thread.$id});
};
$scope.upvote = function(threadId, threadVotes) {
var newVotes = threadVotes + 1;
var ref = new Firebase(fb.url);
var threadRef = ref.child("threads");
threadRef.child(threadId).update({
votes: newVotes
});
}
$scope.downvote = function(threadId, threadVotes) {
var newVotes = threadVotes - 1;
var ref = new Firebase(fb.url);
var threadRef = ref.child("threads");
threadRef.child(threadId).update({
votes: newVotes
});
}
$scope.logout = function(){
userService.logout();
}
});
The threadPageController
angular.module('richWebApp')
.controller('threadPageController', function($scope, $location, $routeParams, threadService, fb, userService){
$scope.$on('handleBroadcast', function (event, args) {
var threadId = args.id;
var currentThread = threadService.getThread(threadId);
currentThread.$bindTo($scope, 'currentThread') //creates $scope.thread with 3 way binding
});
$scope.newComment = '';
$scope.addComment= function(){
if(!$scope.newComment){
return false; //Don't do anything if the text box is empty
}
var currentUser = userService.getLoggedInUser();
var newComment = {
text: $scope.newComment,
username: currentUser.name
};
$scope.currentThread.comments = $scope.currentThread.comments || [];
$scope.currentThread.comments.push(newComment);
$scope.newComment = ''; //Clear the input box
}
});
threadService
angular.module("richWebApp").service("threadService", function($firebaseArray, $firebaseObject, fb){
this.getAllThreads = function(){
var ref = new Firebase(fb.url + '/threads');
return $firebaseArray(ref);
};
this.getThread = function(threadId){
var ref = new Firebase(fb.url + '/threads/' + threadId);
return $firebaseObject(ref);
};
});