Ng-repeat not rendering content - javascript

I'm writing a web app using Node.js+Express to serve (with HoganJs as a templating engine) and AngularJS on the frontend. I'm having problems with ng-repeat rendering the correct number of elements, but without any content in. I broke the ng-repeat down into a smaller example and it's still not rendering.
EDIT: There was a typo in the Plunkr, so I removed it. Here's a more expanded extract from my app.
Here's a section of my view: index.hjs
<div class="search-results" ng-controller="results">
<ul class="tracks">
<li class="track" ng-repeat="track in tracks">
<ul class="meta">
<li>
<div class="name">
<span class="value">{{track.name}}</span>
</div>
</li>
<li>
<div class="album">Album:
<span class="value">{{track.album}}</span>
</div>
</li>
<li>
<div class="artist">Artist:
<span class="value">{{track.artist}}</span>
</div>
</li>
<li>
<div class="length">Length:
<span class="value">{{track.length}}</span>
</div>
</li>
</ul>
</li>
</ul>
</div>
The results controller: js/controllers/results.js
var results = function($scope, socket) {
$scope.tracks = [
{"uri":"spotify:track:1jdNcAD8Ir58RlsdGjJJdx","name":"Ho Hey","artist":"The Lumineers","album":"The Lumineers"},
{"uri":"spotify:track:3uuGbRzMsDI5RiKWKOjqWL","name":"Hey Porsche","artist":"Nelly","album":"Hey Porsche"},
{"uri":"spotify:track:5BSndweF91KDqyxANsZcQH","name":"Ho Hey","artist":"The Lumineers","album":"The Lumineers"},
{"uri":"spotify:track:2UNc0duOP4cS7gqYFFkwxT","name":"Hey Girl","artist":"Billy Currington","album":"Hey Girl"},
{"uri":"spotify:track:6fgbQt13JlpN59PytgTMsA","name":"Snow [Hey Oh]","artist":"Red Hot Chili Peppers","album":"Snow [Hey Oh]"}
];
socket.on("results", function(tracks) {
$scope.tracks = tracks;
console.dir(JSON.stringify($scope.tracks));
});
$scope.add = function(uri) {
socket.emit("add", uri);
};
};
And finally my module: app.js
var app = angular.module("app", []);
var factories = {
socket: socket
};
app.factory(factories);
var controllers = {
actions: actions,
search: search,
results: results,
queue: queue
};
app.controller(controllers);
For testing purposes, the tracks are hardcoded in when the app is run 5 lis are rendered but there no content has been templated inside of them.

Remove a ) here:
];
});
^
Plnkr: http://plnkr.co/edit/e0URFt?p=preview

I've just realised what's happening here. Hogan.js is overwriting angular's templates when the page is rendered at the server.

Just remove the extra bracket added to your script (line 19). Please look at the script here.
http://plnkr.co/edit/sy2m0RuXRudGvjnmfC3Y?p=preview

Related

AngularJS advice on how to fix a bug when pushing two of the same strings into the array

I'm having trouble trying to figure out how to fix a bug that happens when I try to push the same string as added previously to the array. It gets stuck and will not allow the app to post another string.
How do I make sure that your repeat doesn't get stuck when there two of the same values in the array?
Bug Example Screenshot
--> Bug happens when I try to push "1"into the array again after a "1" is already posted.
HTML Code
<body>
<div data-ng-controller="appController" class="container">
<div class="row">
<form>
<label for = "status"> Status: </label>
<input data-ng-model = "input_data" type="text" id="status"/>
<button data-ng-click="add_data()"> OK </button>
<ul class = "list-group">
<li class="list-group-item" data-ng-repeat="x in array_data">
{{x}}
<button data-ng-click = "remove_data($index)">DEL</button>
</li>
</ul>
</form>
</div>
</div>
<script src="framework/js/jquery.min.js"></script>
<!-- All Bootstrap plug-ins file -->
<script src="framework/js/bootstrap.min.js"></script>
<!-- Basic AngularJS -->
<script src="framework/js/angular.min.js"></script>
<!-- Your Controller -->
<script src="framework/js/appstatpost.js"></script>
</body>
AngularJS code
var app = angular.module("myApp", []);
app.controller("appController", function ($scope) {
$scope.array_data = [];
$scope.add_data = function () {
$scope.array_data.push($scope.input_data);
};
$scope.remove_data = function (index) {
$scope.array_data.splice(index, 1);
};
});
You could use track by $index, example:
<li class="list-group-item" data-ng-repeat="x in array_data track by $index">
AngularJS tries by default to find a key in your array to index by. Normally this works well, but if you have duplicates then you have to tell AngularJS to make a new index, in this case, $index.

Angularjs nested controller called just one time

I'm new in Angularjs and I have an app, with some "projects", which have a local menu displayed on some pages. Index.html contains the main navbar with footer :
<body ng-app="ysi-app" ng-controller="MainController">
<div class="page-content">
<div class="row">
<div class="col-md-2">
<div class="sidebar content-box" style="display: block;">
<ul class="nav">
<!-- Main menu -->
<li ng-if="isAuthenticated()">{{name}}</li>
<li class="current">Dashboard</li>
<li>Projects</li>
</ul>
</div>
<div ng-if="isAuthenticated() && displayProjectMenu == true" ng-include="'views/localMenu.html'" ng-controller="LocalMenuController">
</div>
</div>
<div ng-view></div>
</div>
So I have a nested controller LocalMenuController for the local menu and a main controller. The project controller sets the datas :
angular.module('ProjectCtrl',[]).controller('ProjectController',function($scope,$location, ProjectService,$route, AuthenticationService, $rootScope){
$scope.setProjectDatas = function(projectName, projectId){
ProjectService.setName(projectName);
$rootScope.projectId = projectId;
};});
I set the id of one project to the $rootScope for testing (I have a Service which will do that better) and get it in the LocalMenuController :
angular.module('LocalMenuCtrl',[]).controller('LocalMenuController', function($scope, ProjectService, $rootScope) {
$scope.projectId = '';
$scope.projectId = $rootScope.projectId;
});
I display projects in a table and when I clicked on one of it, the function setProjectDatas(name,id) is called. The problem is when I clicked on one project, the id of the project is correct and set but when I go previous and clicked on another project, the id is the old id of the project previously clicked. The datas are not updating. I googled my problem but found nothing on it.
I think the LocalMenuController is called only one time but not after.
What am I doing wrong ?
Thank you
UPDATE
I've created a Directive which displays the template but it's still not updating the partial view localMenu.
LocalMenu Directive :
angular.module('LocalMenuCtrl',[]).controller('LocalMenuController', function($scope, ProjectService, $rootScope) {
console.log('-> LocalMenu controller');
})
.directive('localMenu', function($rootScope){
return {
templateUrl: '/YSI-Dev/public/views/partials/localMenu.html',
link: function(scope){
scope.projectId = $rootScope.projectId;
}
};
});
A part of index.html
<div ng-if="isAuthenticated() && displayProjectMenu == true" ng-controller="LocalMenuController">
<div local-menu></div>
</div>
Partial view localMenu :
<div class="sidebar content-box" style="display: block;">
<ul class="nav">
<li><i class="glyphicon glyphicon-list-alt"></i> Backlog</li>
<li><i class="glyphicon glyphicon-user"></i> My team </li>
</ul>
</div>
I'm trying to get the projectId from the $rootScope and inject it in the <a href="#/project/{{projectId}}" but I have some troubles. What's the way to do that ?
First of all, try using directives instead of ng-controller. You can encapsulate your code and template into a unit. You can also try creating a component. Pass some data to the directive/component and Angular will take care of updating the template and running whatever needs to run within the directive/component. (Given that you used two-way data-bindings)
From the code above, I cannot see what would trigger LocalMenuController to run again.

Dynamic binding of li items click event

I have durandal and knockout web application.
I have a html as follows:
<ul id="header">
</ul>
In .js function I am adding li dynamically as:
$("#header).append('<li id="btn"> <span class="name">Test</span></li>')
ko.applyBindingsToNode(ul);
I am aware of the fact that I am binding the li after applyBindings has been called. To add it dynamically I am using
ko.applyBindingsToNode(ul); , but still no luck.
Can anyone please tell me whats the syntax/alternative solution for this?
// begins a JavaScript comment. This means that everything after <a href="javascript: is commented out, and the resulting code will look something like this:
$("#header).append('<li id="btn"><a href="javascript:
ko.applyBindingsToNode(ul);
Furthermore, the ko.applyBindingsToNode call will be part of the ' string opened just after the opening brackets on the append call.
To resolve this, you need to escape those comments by placing backslashes before them:
href="javascript:\/\/"
Refer to the demo here.
Please find the code below:
HTML:
<ul id="header">
</ul>
JS:
$(function() {
$("#header").append('<li id="btn">' + ' <span class="name">Test</span></li>');
//ko.applyBindingsToNode(ul);
});
using foreach:
var DemoPage = (function() {
function DemoPage() {
var _this = this;
_this.buttons = ko.observableArray([]);
_this.debug = ko.observable('');
_this.testmethod = function(data, event) {
_this.debug('Clicked LI: ' + data.buttonId);
}
_this.addHeadingRow = function() {
_this.buttons.push({
buttonId: Math.floor(Math.random() * 100)
});
}
}
return DemoPage;
})();
var demoApp = new DemoPage();
ko.applyBindings(demoApp);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul id="header" data-bind="foreach: buttons">
<li id="btn">
<a data-bind="click: $root.testmethod">
<span class="name">Test</span>
</a>
</li>
</ul>
<button data-bind="click: addHeadingRow">add heading row</button>
<p data-bind="text: debug"></p>
I would like to answer my own question.
The solution is very simple .
In the .js file define an observable array
self.arraysample = ko.observableArray([]);
In a method populate the array with data
self.arraysample.push(data)
In the html page , we can do this:
<ul id="head" data-bind:"foeach:arraysample">
<li>
<a id="btn">
<span data-bind="text:$data.arrayelement"></span>
</a>
</li>
</ul>
Thats it whenevr the data in the "self.arraysample" changes, automatic updating will take place because of knockout js properties.
I hope it helps someone because ,I have seen so many examples in the web advising to use ko.applyBindings() once again which doesnt work at all.

View different HTML pages?

I wanted to view different HTML pages based on what a user clicks. For example, I have three tabs set up as so:
<div class="span7" >
<ul class="nav nav-tabs" style="margin-bottom: 5px;">
<li class="active">First</li>
<li>Second</li>
<li>Third</li>
</ul>
<div class="span12" style="margin-left:0;" ng-grid="gridOptions"></div>
</div>
And I merely want to view a different page based off of what the individual clicks. For example, if he clicks First, he will see First's html page where source.sourceObject in the code below denotes which html page to view. It is written like so:
<div class="span5">
<div class="edus-activity-container">
<div ng-show="sourceViewState.selected" class="edus-admin-activities-grid" />
</div>
<div ng-include="'/partials/' + source.sourceObject + '.html'"/>
</div>
where in my javascript file, source.sourceObject is defined based off of if I click the First, Second or Third tab. However, my implementation is not working. I made sure I had no typos in the spelling of my files in source.sourceObject. Any ideas on how to do so?
In your controller:
var pages = { 'one': 'partials/one.html', 'two':'partials/two.html' }
$scope.currentPage = pages['one'] ; //This is required if you want a default page
$scope.first = function(){ $scope.currentPage = pages['one']; }
$scope.two = function(){ $scope.currentPage = pages['two']; }
In your template/HTML
<div ng-include="currentPage"/>

AngularJS update model on click of element and also toggle CSS class

New to Angular and a bit confused. I have a list item that needs to display a tick or a cross depending on an initial value from its controller.
When a user clicks the list item I want to change the value to its current opposite and then update the CSS class to reflect this in the DOM.
Currently I have the following controller:
app.controller('SetupSettingsCtrl', ['$scope', '$rootScope', '$location', function ($scope, $rootScope, $location) {
console.log('setup controller loaded');
$scope.data ={
about: {
uie: '439213949123I034',
appVersion: '3.23453'
},
lab: {
sleep: false,
move: true
},
stats: {
optOut: true
}
};
$scope.chkItem = function($event, prop){
console.log(prop);
};
}]);
And the following template partial:
<div class="pure-u-1">
<h1 class="h2 text-center">About</h1>
<p class="text-center">Phone UIE: <span class="text-valid">{{uie}}</span></p>
<p class="text-center">App version: <span class="text-valid">{{appV}}</span></p>
<p class="text-center"><i class="icon-refresh"></i> Manual Update</p>
</div>
<div class="pure-u-1">
<h2 class="text-center">LAB functions</h2>
<section class="view-content">
<ul class="center-block list-bare list-icon-box-chk">
<li class="pointer" ng-class="{'un-chk': !sleep}" ng-model="sleep" ng-click="chkItem($event)">Sleep with phone on bed</li>
<li class="pointer" ng-class="{'un-chk': !move}" ng-model="move" ng-click="chkItem($event)">Movement checker</li>
</ul>
</section>
</div>
<div class="pure-u-1">
<h2 class="text-center">Anonymous Statistics</h2>
<section class="view-content">
<ul class="center-block list-bare list-icon-box-chk">
<li class="pointer" ng-class="{'un-chk': !optOut}" ng-model="optOut" ng-click="chkItem($event)">I do not want anonymous statistics to be geathered for Health research, and healthcare improvement</li>
</ul>
</section>
</div>
I do not now how to pass the model reference to update the $scope value to trigger the change? When I pass the model property reference I get the value.
I need to call the controller method to pass the model value to the server also.
You should do both: toggle the model as well as call the function inside ng-click without the need of passing the model as a parameter. Also you dont need to bind the model to the lis:
<li class="pointer"
ng-class="{'un-chk': !data.lab.sleep, 'chk': data.lab.sleep}"
ng-click="data.lab.sleep = !data.lab.sleep; chkItem($event)">
Sleep with phone on bed
</li>
<li class="pointer"
ng-class="{'un-chk': !data.lab.move, 'chk': data.lab.move}"
ng-click="data.lab.move = !data.lab.move; chkItem($event)">
Movement checker
</li>
(I guess those {{uie}} and {{appV}} in your HTML need to be like {{data.about.uie}} and {{data.about.appVersion}})
JS:
$scope.chkItem = function($event){
/* do something here */
};
In your "Controller" it will be avaliable
$scope.chkItem = function($event, prop){
console.log(data.lab.sleep);
};
You can update directly no need to pass
<li class="pointer" ng-class="{'un-chk': !data.lab.sleep}" ng-model="data.lab.sleep" ng-click="chkItem($event)">Sleep with phone on bed</li>

Categories