infinite loop when trying to retrieve data from controller in view using angular $resource.Query() method - sails.js

I'm building a small sails.js + angular.js app.
Here is a fiddle that roughly shows what my code looks like: http://jsfiddle.net/WEk3F/
index: function(req, res, next) {
Food.find({}, function foundFoods(err, foods) {
if (err) return next(err);
var data = {
name1: "test1",
name2: "test2"
}
res.view(
'food/index', {
foods: data
});
});
},
<div ng-app="myApp">
<div ng-controller="FoodController">
<ul>
<li ng-repeat="food in foods">
{{food.name}}
</li>
</ul>
<form>
<label for="name">Name:</label>
<input name="name" ng-model="editableFood.name" />
</form>
</div>
</div>
My problem is, that whenever i try to retrieve the data from my controller, i don't get those 2 items but instead it renders more and more items and just doesn't stop. even the page gets slow and unresponsive and almost freezes.
When i say
$scope.foods = [{"name": "test1"},{"name": "test2"}];
instead of
$scope.foods = Food.query();
it works. but i want the data to be coming from the backend via the controller.
the other methods (add, update etc) of the angular.js $resource module work fine for me.
/food maps to the index action of my FoodController and just returns some fixed test data

i found out what the problem was.
the angular.js $resource should only be used in a restful way, so the GET request to my food/index should return an array of objects.
in my case this wasn't the case. instead i used the index action of my controller to render a view and the result was therefor html.
so my options are to define a new route, that the $resource takes for the Query() command or define a new action that i use for the view/html stuff and use the index action again for pure restful response.
thx #sam hunter for pointing me to the right direction
the infinite loop that i received is explained here: One dimensional array of strings being parsed to 2d by angular resource
i love stack overflow

Related

Trying to think about how to build a multi step form in angular 2

I am trying to build a small, 3 step form. It would be something similar to this:
The way I did this in react was by using redux to track form completion and rendering the form body markup based on the step number (0, 1, 2).
In angular 2, what would be a good way to do this? Here's what I am attempting at the moment, and I'm still working on it. Is my approach fine? Is there a better way to do it?
I have a parent component <app-form> and I will be nesting inside it <app-form-header> and <app-form-body>.
<app-form>
<app-header [step]="step"></app-header>
<app-body [formData]="formData"></app-body>
</app-form>
In <app-form> component I have a step: number and formData: Array<FormData>. The step is just a index for each object in formData. This will be passed down to the header. formData will be responsible the form data from user. Each time the form input is valid, user can click Next to execute nextStep() to increment the index. Each step has an associated template markup.
Is there a better way to do something like this?
don't overdo it, if it is a simple form you don't need to use the router or a service to pass data between the steps.
something like this will do:
<div class="nav">
</div>
<div id="step1" *ngIf="step === 1">
<form></form>
</div>
<div id="step2" *ngIf="step === 2">
<form></form>
</div>
<div id="step3" *ngIf="step === 3">
<form></form>
</div>
It's still a small template, and you kan keep all of the form and all the data in one component, and if you want to you can replace the ngIf with something that switches css-classes on the step1,2,3 -divs and animate them as the user moves to the next step
If you want to keep things extensible, you could try something like this:
<sign-up>
<create-account
[model]="model"
[hidden]="model.createAccount.finished">
</create-account>
<social-profiles
[model]="model"
[hidden]="model.socialProfiles.finished">
</social-profiles>
<personal-details
[model]="model"
[hidden]="model.personalDetails.finished">
</personal-details>
</sign-up>
export class SignUpVm {
createAccount: CreateAccountVm; //Contains your fields & finished bool
socialProfiles: SocialProfilesVm; //Contains your fields & finished bool
personalDetails: PersonalDetailsVm; //Contains your fields & finished bool
//Store index here if you want, although I don't think you need it
}
#Component({})
export class SignUp {
model = new SignUpVm(); //from sign_up_vm.ts (e.g)
}
//Copy this for personalDetails & createAccount
#Component({})
export class SocialProfiles {
#Input() model: SignUpVm;
}

Row and column method missing on element.all

I have the following setup, where I want Protractor to click on a row (or the checkbox in a row, either is fine):
<li data-ng-repeat="room in chatRooms | ***filters***">
<div ng-click="toggleJoin(room.id)">
<input type="checkbox" value="{{room.id}}" ng-checked="isChecked(room.id)" />
<span>{{room.name}}</span>
</div>
</li>
And I want to do this with my Page Object:
var PageObject = function() {
this.lstChatRooms = element.all(by.repeater('room in chatRooms'));
this.clickChatRoom = function(index) {
this.lstChatRooms.row(index).column('{{room.id}}').click();
};
};
But when I try to call clickChatRoom with some index in my test, I get an error saying the object has no method 'row', and I've seen the same behavior with 'column'. I'm not calling anything on the list of chat rooms prior to this in my test, so the promise should not be resolved at that point. What am I doing wrong?
Update: The issue may be caused by this bug. Unless anyone can see that I'm doing something wrong with the API or something else.
i can't test it right now, but this should work for you:
this.lstChatRooms.then(function(rooms) {
rooms[index].findElement(by.tagName('input')).click();
});

Is there a difference between .add() in AngularJS vs. DOM?

Is there a difference between the .add() method in AngularJS and DOM? I'm using .add() in an AngularJS application and I'm running into *issues pushing data to a firebase. When I go to look at the documentation to do some research I found nothing in the AngularJS docs about this method. I found that .add() in DOM is for adding an option to a select (eg Object_of_Select.add(option, before);). But this doesn't really translate IMO to how I'm using it in AngularJS.
*The Issue I'm experiencing:
I have no problem with Firebase and persisting data, its super straightforward and awesome. I run into problems however, when I take an object (in this example "campaign") and push objects into a nested array of objects ("tactics"). The second I add local data, I can no longer "add" that data to the Firebase. No errors, just nothing.
So if anyone could point me in the right direction it would be super.
TEMPLATE
<!-- TITLE THE CAMPAIGN -->
<h3>{{campaign.name}}</h3>
<input type="text" placeholder="Campaign Title" ng-model="campaign.name"/>
<!-- LOOP THROUGH TACTICS -->
<div class="well well-lg" ng-repeat="foo in campaign.tactics">
<h4>{{foo.name}} - {{foo.type}}</h4>
</div>
<!-- SELECT TACTIC TYPE (Loaded from separate Firebase instance)-->
<select ng-model="tacticSelect">
<option ng-repeat="tactic in tactics" value="{{tactic.name}}">{{tactic.name}} ({{tactic.type}})</option>
</select>
<!-- PUSH NEW TACTICS INTO TACTIC LOOP -->
<button class="btn" ng-click="campaign.tactics.push({'name': tacticSelect, 'type': 'email'})"><span class="glyphicon glyphicon-plus" ></span> Add Tactic</button>
<!-- SAVE CAMPAIGN TO CAMPAIGNS FIREBASE -->
<button class="btn btn-success" ng-click="campaigns.add(campaign)"></span> Save</button><!-- ng-click="campaigns.add(campaign)"-->
CONTROLLER
function scopeAssignments($scope, angularFireCollection, $location){
$scope.tactics = angularFireCollection(fbTactics);
$scope.campaigns = angularFireCollection(fbCampaigns);
// SAMPLE DATA SET
$scope.campaign = {"tactics" : []};
}
DESIRED DATA STRUCTURE (I'm trying to create campaigns that can have multiple tactics with in a single campaign.)
$scope.campaign = {
"name" : "Sample Campaign",
"tactics" : [{
"name" : "First Tactic",
"type" : "Email"
}]
};
add is neither an Angular method nor a DOM method. It's a method provided by an angularFireCollection. angularFireCollection synchronizes data explicitly, which means that calling $scope.campaigns.tactics.push is not sufficient to send that data to Firebase.
You essentially have two options. You can use the angularFire service which will automatically synchronize data whenever it changes locally.
CONTROLLER
function scopeAssignments($scope, angularFire){
$scope.tactics = angularFire(fbTactics);
$scope.campaigns = angularFire(fbCampaigns);
$scope.campaign = $scope.campaigns[campaignID];
$scope.campaign.tactics = [];
}
If you want to stick with angularFireCollection, you have to explicitly notify the collection when you want some data sent to the Firebase server. You can do this, for example, by:
TEMPLATE
<!-- PUSH NEW TACTICS INTO TACTIC LOOP -->
<button class="btn" ng-click="addTactic(campaign, tacticSelect)">
<span class="glyphicon glyphicon-plus"></span> Add Tactic
</button>
CONTROLLER
$scope.addTactic = function(campaign, tactic) {
$scope.campaign.tactics.push({'name': tacticSelect, 'type': 'email'});
$scope.campaigns.update($scope.campaign);
}

fine-uploader resubmit parameters but not file

I'm using fine-uploader to take multiple (large) files and pass the filename along with an additional user-input parameter. I do that by creating a text input box (called 'allele_freq') next to each file and I pass the filename and the allele_freq parameter to my cgi script.
What happens next (or what will happen next) is that I analyse the data in the file, using the allele_freq parameter and then some images are returned to the page for the user to look at.
If the user wants to re-analyse the data with a new allele_freq, all I want to do is to pass the filename along with the new allele_freq, i.e. I don't want to have to upload the file again.
I've pasted my working code below (it uploads multiple files along with user input for each file) and then the code that I can't get to work (it produces a 'resubmit' button, but doesn't appear to do anything), along with some comments/musings within the code.
Any information on how I would do this will be gratefully received. I'm very new to both fine-uploader and Javascript (as you can probably tell), so please feel free to criticise (constructively of course!) any of my code.
Many thanks,
Graham
<link href="fineuploader/fineuploader-3.6.4.css" rel="stylesheet">
<script src="fineuploader/jquery-2.0.1.js"></script>
<script src="fineuploader/jquery.fineuploader-3.6.4.js"></script>
<div id="multiFineUploader"></div>
<div id="triggeredUpload" class="btn btn-primary" style="margin-top: 10px;">
<i class="icon-upload icon-white"></i> Upload now
</div>
<script>
$('#multiFineUploader').fineUploader({
request: {
endpoint: 'src/lib/upload.cgi'
},
autoUpload: false,
text: {
uploadButton: '<i class="icon-plus icon-white"></i> Select Files'
}
})
.on('submitted', function(event, id, name) {
var fileItemContainer = $(this).fineUploader('getItemByFileId', id);
$(fileItemContainer)
.append('<input type="text" name="allele_freq">');
})
.on('upload', function(event, id, name) {
var fileItemContainer = $(this).fineUploader('getItemByFileId', id),
enteredAlleleFreq = $(fileItemContainer).find('INPUT[name="allele_freq"]').val();
$(this).fineUploader('setParams', {allele_freq: enteredAlleleFreq}, id);
});
$('#triggeredUpload').click(function() {
$('#multiFineUploader').fineUploader('uploadStoredFiles');
});
</script>
above code works fine
code below doesn't
<div id="resubmitFreqs"></div>
<div id="retry" class="btn btn-success" style="margin-top: 10px;">
<i class="icon-upload icon-white"></i> Resubmit
</div>
<script>
$('#resubmitFreqs').fineUploader({
request: {
//use a different script as shouldn't need to handle all the upload stuff
endpoint: 'src/lib/resubmit.cgi'
}
)}
//get the information from the allele_freq box. Should it still be in scope?? If not, how do I get at it?
.on('upload', function(event, id, name) {
var fileItemContainer = $(this).fineUploader('getItemByFileId', id),
enteredAlleleFreq = $(fileItemContainer).find('INPUT[name="allele_freq"]').val();
$(this).fineUploader('setParams', {allele_freq: enteredAlleleFreq}, id);
});
$('#retry').click(function() {
//I presumably don't want to use 'uploadStoredFiles', but I'm not sure how to post my new parameters into the resubmit.cgi server-side script
$('#resubmitFreqs').fineUploader('uploadStoredFiles');
});
</script>
It seems like you are trying to bend Fine Uploader into something that it is not. Fine Uploader should probably not be involved with this step of your process, as its job is to upload files to your server. It is not meant to be an all-in-one web application. If you want to send additional data to your server at some point in time after the file has been sent, simply send a POST request with that data via XHR.

#each with call to helper ignores first entry in array

I'm trying to generate a menu with handlebars, based on this array (from coffeescript):
Template.moduleHeader.modules = -> [
{ "moduleName": "dashboard", "linkName": "Dashboard" }
{ "moduleName": "roomManager", "linkName": "Raumverwaltung" }
{ "moduleName": "userManager", "linkName": "Benutzerverwaltung" }
]
The iteration looks like this (from the html code):
{{#each modules}}
<li {{this.isActive this.moduleName}}>
<a class="{{this.moduleName}}" href="#">{{this.linkName}}</a>
</li>
{{/each}}
{{this.isActive}} is defined like this (coffeescript code again):
Template.moduleHeader.isActive = (currentModuleName) ->
if currentModuleName is Session.get 'module'
"class=active"
Based on Session.get 'module' the appropriate menu item is highlighted with the css class active.
On reload, the Session-variable module contains 'dashboard', which is the first entry in that array, however, it does not get the active-class. If I add an empty object as the first item to the modules-array, the 'dashboard'-item is properly highlighted.
The strange thing is, that the first item (dashboard) is rendered fine, but it does not have the active-class, which raises the impression, that the function this.isActive is not called for the first item.
Am I doing it wrong?
Really going out on a whim here but my traditional approach would be below. I don't know whether it will work but it was too big for a comment, yet again it's not a guaranteed fix. Let me know how it goes:
Replace the HTML with
{{#each modules}}
<li {{isActive}}>
<a class="{{moduleName}}" href="#">{{linkName}}</a>
</li>
{{/each}}
Replace the CS with
Template.moduleHeader.isActive = () ->
currentModule = this
currentModuleName = currentModule.moduleName
if currentModuleName is Session.get 'module'
"class=active"
If its rendering, the each block is working correctly. There must be an issue in the isActive helper. Most likely the if block isn't working as you think it should be. Put a console.log in there and see if it is entered, and put another inside the if (printf debugging). I find this is usually the easiest way to do quick debugging of handlebars helpers.
A useful helper in js for inspecting values inside of a context is as follows
Handlebars.registerHelper("debug", function(optionalValue) {
console.log("Current Context");
console.log("====================");
console.log(this);
if (optionalValue) {
console.log("Value");
console.log("====================");
console.log(optionalValue);
}
});
Then you can call this inside an each block and see all the values the current context/a specific value has.