I have made a small app using AngularJS 1.4, that outputs JSON data from a remote source.
Here is the JSON data in question.
Here is my view - the link to ngInfiniteScroll comes in at the top of the container:
<body ng-app="cn" ng-controller="MainCtrl as main">
<div id="header" class="container-fluid">
<p>Camper News</p>
</div>
<div class="container">
<div infinite-scroll="feed.loadMore()" infinite-scroll-distance="3">
<div ng-repeat="newsItem in myData" class="news-row col-sm-12 col-xs-12">
<div class="col-sm-2 col-xs-12">
<div id="img-container">
<img ng-src={{newsItem.author.picture}} id="user-avatar" />
</div>
</div>
<div class="news-text col-sm-10 col-xs-12">
<a href={{newsItem.link}}><em>{{newsItem.headline}}</em></a>
</div>
<div class="col-sm-10 col-xs-12" id="link-description">
{{newsItem.metaDescription}}
</div>
<div class="col-sm-12 col-xs-12" id="bottom-text">
<div class="col-sm-4 col-xs-12" id="author">
<a href='http://www.freecodecamp.com/{{newsItem.author.username}}'>{{newsItem.author.username}}</a>
</div>
<div class="col-sm-4 col-xs-12" id="likes">
<i class="fa fa-thumbs-o-up"></i> {{newsItem.upVotes.length}}
</div>
<div class="col-sm-4 col-xs-12" id="timestamp">
<span am-time-ago={{newsItem.timePosted}} | amFromUnix></span>
</div>
</div>
</div>
</div>
</div>
</body>
I am attempting to stagger the loading and outputting of the data using ngInfiniteScroll. I'm just not sure how it would work:
app.controller('MainCtrl', function($scope, Feed) {
$scope.feed = new Feed();
});
app.factory('Feed', function($http) {
var Feed = function() {
this.items = [];
this.after = '';
};
Feed.prototype.loadMore = function() {
var url = "http://www.freecodecamp.com/news/hot";
$http.get(url).success(function(data) {
var items = data;
// Insert code to append to the view, as the user scrolls
}.bind(this));
};
return Feed;
});
Here is a link to the program on Codepen. The code that relates to ngInfiniteScroll (controller and factory) is currently commented out in favour of a working version, but this of course loads all links at once.
What do I need to insert in my factory in order to make the JSON load gradually?
From what I can tell, by looking at FreeCodeCamps' story routes, there is no way to query for specific ranges of the "hot news" stories.
It looks to just return json with a fixed 100 story limit.
function hotJSON(req, res, next) {
var query = {
order: 'timePosted DESC',
limit: 1000
};
findStory(query).subscribe(
function(stories) {
var sliceVal = stories.length >= 100 ? 100 : stories.length;
var data = stories.sort(sortByRank).slice(0, sliceVal);
res.json(data);
},
next
);
}
You can use infinite scroll for the stories it does return to display the local data you retrieve from the request in chunks as you scroll.
Related
I'm a bit new to knockout. I'm trying to get a custom component to dynamically load another custom component. I have a variable called location_board that contains html and that html has a custom component in it. . When I use the data-bind="html: location_board" it put the line for the in the dom but it doesn't run the custom component to fill out that node. Note: If I add the npc-widget directly to the template it works. It just doesn't work when it is added though the html binding. From my research I think this means I need to applyBindings on it? I'm not sure how to go about that in this situation though.
Any help is apricated.
Here is my full code for the custom component.
import {Database} from './database.js'
let database = new Database();
import {ResourceManager} from "./resource-manager.js";
let resourceManager = new ResourceManager();
let locationRegister = {
fog_forest: {
name: "The Ghostly Woodland",
image: "url('img/foggy_forest.jpeg')",
description: `
Place holder
`,
location_board: `
<npc-widget id="john-npc" params="id: 1, tree: 'shopkeep', speed: 50"></npc-widget>
<div>In</div>
`
}
};
ko.components.register('location-widget', {
viewModel: function (params) {
let self = this;
self.function = function () {
console.log("Functions!")
}
for(let k in locationRegister[params.id]) {
console.log(k)
this[k] = locationRegister[params.id][k];
}
console.log(this.name)
//return { controlsDescendantBindings: true };
},
template:
`
<div class="row">
<div class="col-lg-12">
<h2 id="title" class="tm-welcome-text" data-bind="html: name"></h2>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div data-bind="style: { 'background-image': image}" class="location-picture mx-auto d-block">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="location-description mx-auto d-block" data-bind="html: description"></div>
</div>
</div>
</div>
<hr>
<div class="row">
<div class="col-lg-12">
<div id="location_board" data-bind="html: location_board">
</div>
</div>
</div>
`
});
I trying to append ng-repeat into my HTML div on load.
It recognizes the ng-repeat, however the index value isn't displaying as it should
HTML
<div id="myInnerCarousel"></div>
controller:
var myCarousel = document.getElementById('myInnerCarousel');
var newItem = document.createElement("div");
newItem.setAttribute("class","item");
var newData = document.createElement("div");
newData.setAttribute("class", "col-xs-6 col-sm-6 col-md-3 col-lg-3");
newData.setAttribute("ng-repeat", "mat in " + arr);
//Create individual values seperately
var newMaterialName = document.createElement("h4");
var nameNode = document.createTextNode("{{mat.Name}}");
newMaterialName.appendChild(nameNode);
//Append everything together
newData.appendChild(newMaterialName);
newItem.appendChild(newData);
myCarousel.appendChild(newItem);
However the result is this:
https://gyazo.com/00b76c6b910d4c6701059d404783f720
It got the right idea of displaying my array, however angular isn't displaying h4 right.
EDIT: imtrying to achieve this in my html:
<div ng-controller="myController">
<div class="item">
<div class="col-xs-6 col-sm-6 col-md-3 col-lg-3" ng-repeat="mat in array1">
<h4>{{mat.Name}}</h4>
</div>
</div>
<div class="item">
<div class="col-xs-6 col-sm-6 col-md-3 col-lg-3" ng-repeat="mat in array2">
<h4>{{mat.Name}}</h4>
</div>
</div>
Lets simply state that this is not the way to do it.
A better and more angular way would be (assuming your apps name is app).
HTML
<div ng-controller="myController">
<div class="item">
<div class="col-xs-6 col-sm-6 col-md-3 col-lg-3" ng-repeat="mat in array">
<h4>{{mat.Name}}</h4>
</div>
</div>
</div>
Angular Controller
angular.module('app').controller('myController', function($scope) {
$scope.array = [{Name: 'abc'}, {Name: 'def'}]; // or whatever your data looks like.
});
Dynamically added component should be compiled using angular. you can use $compile function of angular. Below is working code.
Jsfiddle
function TodoCtrl($scope, $compile) {
$scope.arr = [{Name: "Dynamically Added cowermponent"}];
var myCarousel = document.getElementById('myInnerCarousel');
var newItem = document.createElement("div");
newItem.setAttribute("class","item");
var newData = document.createElement("div");
newData.setAttribute("class", "col-xs-6 col-sm-6 col-md-3 col-lg-3");
newData.setAttribute("ng-repeat", "mat in arr");
//Create individual values seperately
var newMaterialName = document.createElement("h4");
var nameNode = document.createTextNode("{{mat.Name}}");
newMaterialName.appendChild(nameNode);
//Append everything together
newData.appendChild(newMaterialName);
newItem.appendChild(newData);
myCarousel.appendChild(newItem);
$compile(newItem)($scope);
}
I am trying to make a UI using list of product in a json(products.json) file on local and their availability in number from wp rest api call back in html(ionic) I have this:
Controller:
.controller('ShopCtrl', function($scope, $ionicActionSheet, BackendService, CartService, $http, $sce ) {
$scope.siteCategories = [];
$scope.cart = CartService.loadCart();
$scope.doRefresh = function(){
BackendService.getProducts()
.success(function(newItems) {
$scope.products = newItems;
console.log($scope.products);
})
.finally(function() {
// Stop the ion-refresher from spinning (not needed in this view)
$scope.$broadcast('scroll.refreshComplete');
});
};
$http.get("http://example.com/wp-json/wp/v2/categories/").then(
function(returnedData){
$scope.siteCategories = returnedData.data;
console.log($scope.siteCategories);
}, function(err){
console.log(err);
});
Template view:
<div ng-repeat = "product in products">
<div class="item item-image" style="position:relative;">
<img ng-src="{{product.image}}">
<div ng-repeat = "siteCategory in siteCategories">-->
<button class="button button-positive product-price" ng-click="addProduct(product)">
<p class="white-color product-price-price">post <b>{{siteCategory[$index].count}}</b></p>
</button>
</div>
</div>
<div class="item ui-gradient-deepblue">
<h2 class="title white-color sans-pro-light">{{product.title}} </h2>
</div>
<div class="item item-body">
{{product.description}}
</div>
</div>
so how can I achieve that? I tried to use nested ng-repeat but it didn't work out.
I'm using this plugin called Dragula which needs an ObservableArray as a source for the data..
HTML/Knockout-bindings
<div class="widget-container">
<div class="widget-content visible" id="team-setup">
<div class="header">
<p>Lagoppsett</p>
<p></p>
</div>
<div class="col-sm-12">
<div class="row">
<div class="widget-container">
<div class="col-xs-6 widget-content visible">
<div class="header">
<p>Tilgjengelige spillere</p>
<p></p>
</div>
<div class="player-card-container-mini" data-bind="dragula: { data: availablePlayers, group: 'playerz' } ">
<div class="player-card-mini">
<div class="player-card-left">
<div class="player-avatar" style="margin-left: 85%;">
<img src="Content/Images/player-female.png" id="imgAvatar" runat="server" />
<div class="player-shirt-no" data-bind="text: ShirtNo"></div>
</div>
</div>
<div class="player-card-subtext">
<div class="player-text">
<div class="player-card-header-small" data-bind="text: PlayerName"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xs-6 widget-content visible">
<div class="header">
<p>Lag</p>
<p></p>
</div>
<div data-bind="foreach: teamsetup">
<div data-bind="foreach: SubTeams">
<h1 data-bind="text: TeamSubName"></h1>
<div class="player-card-container-mini" data-bind="dragula: { data: Players, group: 'playerz' } " style="border: 1px solid red; min-height:200px">
<div class="player-card-mini">
<div class="player-card-left">
<div class="player-avatar" style="margin-left: 85%;">
<img src="Content/Images/player-female.png" id="img1" runat="server" />
<div class="player-shirt-no" data-bind="text: ShirtNo"></div>
</div>
</div>
<div class="player-card-subtext">
<div class="player-text">
<div class="player-card-header-small" data-bind="text: PlayerName"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="clear:both"> </div>
</div>
</div>
Knockout code :
var TeamSetupViewModel = function () {
var self = this;
self.teamsetup = ko.observableArray();
self.availablePlayers = ko.observableArray();
self.testPlayers = ko.observableArray();
}
var model = new TeamSetupViewModel();
ko.applyBindings(model, document.getElementById("team-setup"));
var uri = 'api/MainPage/GetTeamSetup/' + getQueryVariable("teamId");
$.get(uri,
function (data) {
model.teamsetup(data);
model.availablePlayers(data.AvailablePlayers);
model.testPlayers(data.AvailablePlayers);
console.log(data);
}, 'json');
});
The problem is... that i'm having a ObservableArray at the top node, and i do need ObservableArrays further down in the hierarchy.
model.availablePlayers works fine, but when accessing the other players in the html/ko foreach loops through teamsetup -> SubTeams -> Players it doesn't work due to Players isn't an ObservableArray. (There might be everyting from 1 to 7 SubTeams with players).
So how can i make the Players in each SubTeams an ObservableArray ?
See the image for the datastructure :
You could use Mapping plugin, but if players is the only thing you need, you can do it manually:
Simplify your view model:
var TeamSetupViewModel = function () {
var self = this;
self.availablePlayers = ko.observableArray();
self.subTeams = ko.observableArray();
}
After you get the data from the server, populate the view model converting the array of players on every team to an observable array of players:
$.get(uri,
function (data) {
model.availablePlayers(data.AvailablePlayers);
model.subTeams(data.SubTeams.map(function(t) { t.Players = ko.observableArray(t.Players); return t; }));
}, 'json');
});
Finally, remove the following line in your template (with its closing tag) - nothing to iterate over anymore:
<div data-bind="foreach: teamsetup">
... and update the name of the property in the next line, so it is camel case like in the VM:
<div data-bind="foreach: subTeams">
main.html
<div class="row" ng-repeat="post in myBlogPosts.slice().reverse()">
<br>
<div class="col-md-9 text-center">
<a href="#/blog-post/{{post._id}}">
<div class="thumbnail mTextBg customShadow">
<br>
<img class="img-responsive" src="http://placekitten.com/700/400" alt="">
<div class="caption">
<h3>{{post.imdbId}}</h3>
<p>{{post.blogContent}}</p>
</div>
</div>
</a>
</div>
<div class="col-md-3">
// I WANT THIS PART !!
<div class="well sideBars customShadow">
<img class="img-responsive" ng-src="{{film.Poster}}" title="{{film.Title}}">
<h4 class="text-center">{{film.Title}}</h4>
<p class="text-center" style="margin-bottom: 2px;"><b>Year:</b> {{film.Year}}</p>
<p class="text-center"><span class="customMargin">Runtime: {{film.Runtime}}</span></p>
<p class="text-center"><span class="customMargin">Director: {{film.Director}}</span></p>
<p class="text-center"><span class="customMargin">Writer: {{film.Writer}}</span></p>
<p class="text-center"><span class="customMargin">Actors: {{film.Actors}}</span></p>
</div>
</div>
</div>
This is part of my main.html . In h3 and p tags, I get imdbId and blogContent from my database and put it in ng-repeat in order to traverse blog posts in list. I want to be able get other information(under // I WANT THIS PART) for every post in myBlogPost.
MainController.js
var refresh = function() {
$http.get('/myDatabase').success(function(response) {
$scope.myBlogPosts = response;
});
};
refresh();
This part work as expected when page loaded.
I need also these parts in Main Controller ;
var onGetFilmData = function (data) {
$scope.film = data;
};
var onError = function (reason) {
$scope.error = reason;
};
imdb.getImdbInfo(-- need Id --).then(onGetFilmData, onError);
But I need to put each post id somehow in order to get specific data from Imdb api.
Imdb.js
(function(){
var imdb = function($http){
var getImdbInfo = function (id) {
return $http.get('http://www.omdbapi.com/?i=' + id + '&plot=short&r=json')
.then(function(response){
return response.data;
});
};
return{
getImdbInfo: getImdbInfo
};
};
var module = angular.module('myApp');
module.factory('imdb', imdb);
})();
If I delete id part and put a specific id string in getImdbInfo function, all post in main.html fill with just one film information. I want to fetch those data for each film in my database(I am holding imdb id of each film in my database).
MainController
var jsonObj = {};
var refresh = function() {
$http.get('/myDatabase').success(function(response) {
jsonObj = response;
for(var i = 0; i < jsonObj.length ; i++){
jsonObj[i].title = '';
}
for(var i = 0; i < jsonObj.length ; i++){
(function(i) {
imdb.getImdbInfo(jsonObj[i].imdbId).then(function (data) {
jsonObj[i].title = data.Title;
});
})(i);
}
$scope.myBlogPosts = jsonObj;
});
};
refresh();
main.html
<div class="row" ng-repeat="post in myBlogPosts.slice().reverse()">
<br>
<div class="col-md-9 text-center">
<a href="#/blog-post/{{post._id}}">
<div class="thumbnail mTextBg customShadow">
<br>
<img class="img-responsive" src="http://placekitten.com/700/400" alt="">
<div class="caption">
<h3>{{post.imdbId}}</h3>
<p>{{post.blogContent}}</p>
</div>
</div>
</a>
</div>
<div class="col-md-3">
<!-- Side Widget Well -->
<div class="well sideBars customShadow">
<h4 class="text-center">{{post.title}}</h4>
</div>
</div>
</div>
I solve my problem with adding response from Imdb to my json object which is coming from database. So I can easily use them in ng-repeat.