Filtering directive in Angular.js - javascript

I'm trying to develop a single web application which is a blog, displaying posts. They are included in a template by the ng-repeat directive:
<div class="post" data-ng-repeat="post in postList ">
<div class="date">published: {{post.published_at | date:'dd-MM-yyyy, HH:mm'}}</div>
<a class="btn btn-default" data-ng-click="editPost(post)"><span class="glyphicon glyphicon-pencil"></span></a>
<a class="btn btn-default" data-ng-click="deletePost(post)"><span class="glyphicon glyphicon-remove"></span></a>
<h1>{{post.title}}</h1>
<p>{{post.text}}</p>
</div>
</div>
They have fields, such as title, text and publishing date, defined in the controller. I'd like to filter them by various criteria. For this purpose, I tried to implement my own custom filter (so that I can filter by more than 1 field):
angular.module("blog").
filter('bytitle', function() {
return function(posts, title) {
var out = [];
// Filter logic here, adding matches to the out var.
var i;
for(i = 0; i < posts.length; i++){
if(posts[i].title.indexOf(title) >=0){
out.push(posts[i]);
}
}
return out;
}
});
however, if I run the javascript console, I get the following error, caused only by the presence of the code above:
Argument 'postController' is not a function, got undefined
I'm new to angular, and I'm not really sure what this means. Any ideas?
The entire source code: http://plnkr.co/edit/suATcx8dQXZqcmmwlc0b?p=catalogue
Edit 2: The problem is partially solved. I added this filter functionality:
<div class="post" data-ng-repeat="post in postList | bytitle : filterTerm">
but something goes wrong while running the script:
TypeError: Cannot read property 'length' of undefined
It occurs at line 7 (the one with posts.length).

in you file with filters instead angular.module("blog", []) you need angular.module("blog").
in first case - you create module in second - get.
see doc:
When passed two or more arguments, a new module is created. If passed only one argument, an existing module (the name passed as the first argument to module) is retrieved.
sidenote: in plunker you have wrong reference to js files
You have error with length property, because before loading posts by ajax, you not init this variables, so in filter passed undefined.
You can modify your filter like
angular.module("blog").
filter('bytitle', function() {
return function(posts, title) {
var out = [];
//if not pass posts - return empty
if(!posts) return out;
//if not pass title, or it empty - return same collection
if(!title) return posts;
// Filter logic here, adding matches to the out var.
var i;
for (i = 0; i < posts.length; i++) {
if (posts[i].title.indexOf(title) >= 0) {
out.push(posts[i]);
}
}
return out;
}
});
var app = angular.module("blog", []);
app.controller("postController", function($scope, $http, $timeout) {
var path = 'http://private-79b25-blogtt.apiary-mock.com';
$scope.titleFilter = "";
$scope.contentFilter = "";
$http.get(path + '/posts')
.success(function(data, status, headers, config) {
$timeout(function() {
$scope.postList = data;
});
})
.error(function(data, status, headers, config) {
console.log("error getting " + status);
});
$scope.form_header = "New post";
$scope.addPost = function() {
var post = {
title: $scope.title,
text: $scope.text,
published_at: new Date()
};
$http.post(path + '/posts', post)
.success(function(data, status, headers, config) {
$scope.postList.push(post);
})
.error(function(data, status, headers, config) {
console.log("error posting " + status);
});
$scope.title = "";
$scope.text = "";
};
$scope.deletePost = function(post) {
var del = confirm("Are you sure you want to delete or modify this post?");
if (del) {
var i = $scope.postList.indexOf(post);
$scope.postList.splice(i, 1);
}
};
var backupPostContent;
$scope.editPost = function(post) {
$scope.deletePost(post);
$scope.form_header = "Edit post";
$scope.title = post.title;
$scope.text = post.text;
backupPostContent = post;
};
$scope.cancelEdit = function() {
$http.post(path + '/posts', backupPostContent)
.success(function(data, status, headers, config) {
$scope.postList.push(backupPostContent);
$scope.form_header = "New post";
})
.error(function(data, status, headers, config) {
console.log("error posting " + status);
});
$scope.title = "";
$scope.text = "";
};
$scope.filter = function(term) {
}
});
angular.module("blog").
filter('bytitle', function() {
return function(posts, title) {
var out = [];
if(!posts) return out;
if(!title) return posts;
// Filter logic here, adding matches to the out var.
var i;
for (i = 0; i < posts.length; i++) {
if (posts[i].title.indexOf(title) >= 0) {
out.push(posts[i]);
}
}
return out;
}
});
#wrap {
width: 600px;
margin: 0 auto;
}
#left_col {
float: left;
width: 300px;
}
#right_col {
float: right;
width: 300px;
}
body {
padding: 0px 15px;
}
.row-centered {
text-align: right;
}
.page-header {
background-color: #cb892c;
margin-top: 0;
padding: 20px 20px 20px 40px;
}
.page-header h1,
.page-header h1 a,
.page-header h1 a:visited,
.page-header h1 a:active {
color: #ffffff;
font-size: 36pt;
text-decoration: none;
}
.content {
margin-left: 40px;
}
h1,
h2,
h3,
h4 {
font-family: Helvetica, sans-serif;
}
.date {
float: right;
color: #828282;
}
.save {
float: right;
}
.post-form textarea,
.post-form input {
width: 60%;
}
.top-menu,
.top-menu:hover,
.top-menu:visited {
color: #ffffff;
float: right;
font-size: 26pt;
margin-right: 20px;
}
.post {
margin-bottom: 70px;
}
.post h1 a,
.post h1 a:visited {
color: #000000;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="blog" ng-controller="postController">
<div id="wrap">
<div id="left_col">
<h3> Search </h3>
<p>
<input data-ng-model="filterTerm" />
</p>
</div>
<div id="right_col">
<div id="wrap">
<div id="left_col">
<input type="checkbox" value="topic" id="title" ng-model="titleFilter" />In topics
<br>
<input type="checkbox" value="content" id="content" />In contents
<br>
<input type="checkbox" value="content" id="content" />In tags
<br>Between
<input type="text" type="text" class="datepicker" />
</div>
<div id="right_col">
<br>
<br>
<br>and
<input type="text" type="text" class="datepicker" />
<br/>
</div>
</div>
</div>
</div>
<div class="content container" style="padding-top: 50px">
<div class="row">
<div class="col-md-8 col-centered">
<div class="post" data-ng-repeat="post in postList | bytitle : filterTerm ">
<div class="date">published: {{post.published_at | date:'dd-MM-yyyy, HH:mm'}}</div>
<a class="btn btn-default" data-ng-click="editPost(post)"><span class="glyphicon glyphicon-pencil"></span></a>
<a class="btn btn-default" data-ng-click="deletePost(post)"><span class="glyphicon glyphicon-remove"></span></a>
<h1>{{post.title}}</h1>
<p>{{post.text}}</p>
</div>
</div>
<div class="col-md-4 col-centered">
<h1>New post</h1>
<form class="post-form">
<h4>Title:</h4>
<p>
<input type="text" name="title" data-ng-model="title">
</p>
<h4>Text:</h4>
<p>
<textarea name="text" data-ng-model="text"></textarea>
</p>
<button type="submit" class="save btn btn-default" ng-click="addPost()">Save</button>
<button type="reset" class="btn btn-default">Clear</button>
<button type="button" class="btn btn-default" ng-click="cancelEdit()">Cancel edit</button>
</form>
</div>
</div>
</div>
</div>

EDIT
I didn't see the answer from #grundy before making my comments or edits, so it should be accepted as the answer, but I wanted to point out two things:
Working Plunker
My preferred approach is to use the angular.isDefined / angular.isArray:
angular.module("blog").
filter('bytitle', function() {
return function(posts, title) {
if(angular.isDefined(title) && angular.isArray(posts)) {
var out = [];
// Filter logic here, adding matches to the out var.
var i;
for(i = 0; i < posts.length; i++){
if(posts[i].title.indexOf(title) >=0){
out.push(posts[i]);
}
}
return out;
} else {
return posts;
}
}
});
Second, I just wanted to point out that while writing your own filters is sometimes necessary and certainly a great skill to master, the easiest way to filter on a single property is to use the built in filter filter by adding the property to the model value that you want to search on:
<input data-ng-model="filterTerm.title" />
<input data-ng-model="filterTerm.text" />
and then in your repeat add the filter using just the object name as follows:
<div class="post" data-ng-repeat="post in postList | filter: filterTerm ">
You can then use the same filter for multiple properties.

Related

jQuery/JS: delete object from array and DOM

I am learning JS and jQuery. As an exercise, I am trying to create a basic contact list. I need to be able to add and delete contacts from the list.
But I am having some bugs in my result. I can't find the cause of it. If anyone can advise? I would be most gratefull.
BUG 1:
When running the snippet below, you will see some sample contacts generated in the list. You can delete a contact just fine. You can also add a contact. But after adding a contact, the delete buttons stop working.
First I thought the issue was probably a missing character in my .html() method on the contacts objects. But I see no errors there. Anyone?
BUG 2:
Inside the $renderContacts function. You can see the const html. This should replace the let html and for loop below. And it works for long list of contacts. But the first contact is rendered as [object Object]. I don't see the cause. Might these 2 bugs be related?
Please advise.
Many thanks! :)
$(document).ready(function() {
// Array to store all contacts
let contactsArr = [];
// counter to create incrementing ID
let contactID = 0;
// Constructor for contact objects
function Contact(firstName, lastName, email, phone, address) {
this._id = contactID += 1;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.phone = phone;
this.address = address;
contactsArr.push(this);
};
// Some getters, setters and a methods for contact objects
Contact.prototype = {
constructor: Contact,
set(id) {
console.log(`ID is generated on input and may not be changed`)
},
get id() {
return this._id;
},
set firstName(firstName) {
this._firstName = firstName;
},
get firstName() {
return this._firstName;
},
set lastName(lastName) {
this._lastName = lastName;
},
get lastName() {
return this._lastName;
},
set email(emailaddress) {
this._email = emailaddress;
},
get email() {
return this._email;
},
set phone(phone) {
this._phone = phone;
},
get phone() {
return this._phone
},
set address(address) {
this._address = address;
},
get address() {
return this._address
},
toHTML() {
const renderCell = (content, cssClass = "") => `<div class="table-cell ${cssClass}">${content}</div>`;
const deleteContact = `<span title="Delete contact" class="delete-contact far fa-trash-alt fa-sm"></span>`;
const rowActions = renderCell(deleteContact, "text-right contact-actions");
return '<div class="table-row">' +
renderCell(this.id, "contact-id text-right") +
renderCell(this.firstName, "first-name") +
renderCell(this.lastName, "last-name") +
renderCell(this.email, "email") +
renderCell(this.phone, "phone") +
renderCell(this.address, "address") +
rowActions + '</div>';
},
};
// SAMPLE CONTACTS
new Contact("John", "Cubico", "mymail#mail.com", "111-555-6666", "Belgium");
new Contact("Lisa", "The Sailor", "mymail#mail.com", "111-666-7777", "Spain");
new Contact("Christophe", "From next door", "mymail#mail.com", "111-777-8888", "Germany");
new Contact("Aïsha", "From elsewere", "mymail#mail.com", "111-888-9999", "Brussels, Holland");
// Render Samples
function $renderContacts(arr = contactsArr) {
//const html = arr.reduce((all, one) => all += one.toHTML()); // ==>> 1st not rendering
let html = ``;
for (let i = 0; i < arr.length; i++) {
html += arr[i].toHTML();
};
$("#contacts-list").append(html);
};
$renderContacts();
// BUTTONS & ACTIONS
// Add contact
$("#add-contact").on("click", () => {
const $firstName = $("#first-name").val();
const $lastName = $("#last-name").val();
const $email = $("#email").val();
const $phone = $("#phone").val();
const $address = $("#address").val();
const contact = new Contact($firstName, $lastName, $email, $phone, $address); // create contact
$("#contacts-list").append(contactsArr[contactsArr.length - 1].toHTML()); // add contact to DOM
});
// Delete contact
$(".delete-contact").on("click", (event) => {
const arr = contactsArr.slice();
const $id = Number($(event.currentTarget).closest(".table-row").find(".contact-id").text());
const i = arr.findIndex(contact => contact.id == $id);
contactsArr = arr.slice(0, i).concat(arr.slice(i + 1)); // delete from array of contacts
$(event.currentTarget).closest(".table-row").remove(); // delete only this contact from DOM
});
});
html {}
.active {}
.inactive {
color: #b8b8b8;
}
.table td,
.table th {
padding: 0.5rem;
}
.text-large {
font-size: 1.5rem;
}
.relative {
position: relative;
}
/*** Search ***/
#search-input {
font-size: 2rem;
font-weight: 300;
}
span#search-btn {
position: absolute;
right: 0;
top: 0;
width: 50px;
height: 47px;
padding: 10px;
box-sizing: border-box;
font-size: 1.6rem;
line-height: 34px;
}
/*** Contacts table ***/
.table-header {
padding: 15px 0 5px;
}
.table-row {
display: grid;
grid-template-columns: 30px repeat(5, 1fr) 100px;
grid-column-gap: 20px;
margin: 3px 0;
padding: 2px 0;
background: #f0f0f0;
}
.table-cell {
padding: 3px;
}
.editable-cell {
background: rgba(255, 255, 255, 0.6);
}
.contact-actions span {
line-height: 18px;
padding: 3px;
display: inline-block;
width: 26px;
text-align: center;
}
/*** Form ***/
#form-new-contact {
align-items: end;
margin: 0 -15px;
padding: 15px;
}
#add-contact {
width: 100%;
}
<html>
<head>
<title>jQuery contacts app</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<header>
<div class="container text-center mt-4 mb-4">
<h1>jQuery contacts app</h1>
</div>
</header>
<main>
<div class="container">
<form id="form-new-contact" action="" method="POST" class="table-row mt-4 mb-4">
<div class="text-center"><span class="fas fa-user-plus fa-sm mb-2"></span></div>
<div class="">
<label for="first-name">First name:</label>
<input name="first-name" id="first-name" class="form-control form-control-sm" type="text" value="Voornaam" placeholder="John" required>
</div>
<div class="">
<label for="last-name">Last name:</label>
<input name="last-name" id="last-name" class="form-control form-control-sm" type="text" value="Achternaam" placeholder="Doe" required>
</div>
<div class="">
<label for="email">Email:</label>
<input name="email" id="email" class="form-control form-control-sm" type="email" value="E-mailadres" placeholder="john#doe.com" required>
</div>
<div class="">
<label for="phone">Phone:</label>
<input name="phone" id="phone" class="form-control form-control-sm" type="tel" value="Telefoon/GSM" placeholder="555-666-8989" required>
</div>
<div class="">
<label for="address">Address:</label>
<input name="address" id="address" class="form-control form-control-sm" type="text" value="Adres" placeholder="Somewhere" required>
</div>
<div class="">
<button id="add-contact" type="button" class="btn btn-sm btn-success"><i class="fas fa-plus-circle mr-2"></i>Add</button>
</div>
</form>
<div class="table">
<div class="table-row table-header">
<div id="id-header" class="text-right">
<h3 class="h6">ID</h3>
</div>
<div id="first-name-header" class="">
<h3 class="h6">Firstname</h3>
</div>
<div id="last-name-header" class="">
<h3 class="h6">Lastname</h3>
</div>
<div id="email-header" class="">
<h3 class="h6">Email</h3>
</div>
<div id="phone-header" class="">
<h3 class="h6">Phone</h3>
</div>
<div id="address-header" class="">
<h3 class="h6">Address</h3>
</div>
<div class="text-right">
<h3 class="h6">Actions</h3>
</div>
</div>
<div id="contacts-list" class="">
</div>
</div>
</div>
</main>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
BUG1:
You can't fire on click function on dynamically generated DOM element like that.
Add event listener to your element (using pure JS):
elem.addEventListener("click", func, false);
Or change syntax of your click function to this (using jQuery):
$(document).on("click",'.delete-contact', (event) => {
// your code here
});
BUG2:
I don't know why this function acts like that, but you always have to pass default string parameter. Look to the next lines I made:
let html = arr.reduce((all, one) => all += one.toHTML(), '');
Working fiddle

Remove Duplicate Text in Textarea

With the help of some people on this website I managed to create a hashtag counter here - https://codepen.io/timothycdavis17/pen/ZJeVBj.
Here is the code.
HTML
<section class="section-textarea bg-primary">
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class="well textarea-well clearfix">
<textarea rows="16" class="form-control"></textarea>
<div class="buttons">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="button" class="btn btn-secondary btn-primary btn-lg
count-button">Count</button>
<button type="button" class="btn btn-secondary btn-danger btn-lg
reset-button">Reset</button>
</div>
</div>
<div class="remaining-counter">Hashtags Counted: <span
class="well text-well">0</span></div>
</div>
</div>
</div>
</div>
</section>
CSS
body {
background: #0275D8;
}
.textarea-well {
color: #000;
text-align: center;
margin: 10% auto;
}
textarea {
margin-bottom: 20px;
}
.text-well {
background: lightblue;
}
.count-button {
margin-bottom: 20px;
}
.remaining-counter {
font-size: 30px;
margin-bottom: 20px;
}
jQuery
$(".count-button").click(function() {
var text = $("textarea").val();
var count = (text.match(/(^|\W)(#[a-z\d A-Z\D][\w-]*)/g) || []).length;
$('.text-well').html(count);
var seen = {};
$('textarea').each(function() {
var txt = $(this).text();
if (seen[txt])
$(this).remove();
else
seen[txt] = true;
});
}).trigger('click');
$(".reset-button").click(function() {
$("textarea").val(' ');
$('.text-well').html(0);
}).trigger('click');
After it was made I was asked whether it would be possible to not count any duplicate hashtags that are entered into the textarea and maybe error underline theme with a red squiggly line.
I've had a look around this website for answers but couldn't seem to find one that worked.
Any help is appreciated.
Just use a temp array to filter out the duplicates.
// save the matches instead of immediately counting
var tags = text.match(/(^|\W)(#[a-z\d A-Z\D][\w-]*)/g) || [];
// init empty array for holding unique matches
var uTags = [];
// loop through matches
$.each(tags, function(index, value){
// if match is not in holding array array, add it to holding array
if(uTags.indexOf(value) == -1) uTags.push(value);
});
// get count from holding array which will only have one copy of each match
var count = uTags.length;
Here is a fork of your codepen with the above code.

why my h1 title hides behind input box when slideUp executes

I have the following page, which is a wikisearch page that queries multiple wikipidia pages for the search term. The page has the title and input box somewhere around the middle; however, when I click on the botton, the title slides up, and so the input box. But the input box slides all way up covering the title. I think!... how can I prevent the inputbox from covering the title? or make the title stays at the top of page? Thanks
$(document).ready(function() {
//bringing focus to search box
window.load = function() {
document.getElementById("search-box").focus();
};
//listener for search button
$("#search").click(function() {
$("#title").slideUp(3000);
// $("#title").css("text-align", "left");
search();
});
function search() {
//grabbing the id of search result div
var srchResult = document.getElementById("results");
//string entered by user for search
var searchStr = document.getElementById("search-box").value;
//replace space with _ in search query
searchStr = searchStr.replace(" ", "_");
console.log(searchStr);
$.ajax({
url: "https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=" + searchStr + "&prop=info&inprop=url&utf8=&format=json",
dataType: "jsonp",
success: function(response) {
if (response.query.searchinfo.totalhits === 0) {
showError(searchStr);
} else {
displayResults(response);
}
},
error: function() {
alert("Something went wrong.. <br>" +
"Try again!");
}
});
function displayResults(response) {
console.log(response.query);
var search = response.query.search;
var srchLength = response.query.search.length;
srchResult.innerHTML = "";
// console.log(srchResult.innerHTML);
//pulling title and searchbox to top
// $("#title").css("margin-top:", "10px !important");
for (var i = 0; i < srchLength; i++) {
srchResult.innerHTML += '<div class="output"><h4>' + search[i].title + ' </h4><p>' + search[i].snippet + '</p></div>';
}
}
return false;
}
function showError(search) {
srchResult.innerHTML = '<div class="output text-center"><h4>No Search result for: ' + search + '</h4></div>';
}
});
body {
background-color: #495444;
}
search-input {
width: 90%;
}
center {
align-left: auto;
align-right: auto;
text-align: center;
}
.output {
background-color: white;
border-color: black;
border-width: 1px;
border-style: solid;
opacity: 0.5;
margin-top: 10px;
}
h1 {
margin-top: 200px;
color: #1484e5;
font-family: 'Josefin Sans', sans-serif;
font-size: 50px;
padding-bottom: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Josefin+Sans" rel="stylesheet">
<div class="container ">
<h1 id="title" class="text-center"><strong>WikiSearch</strong></h1>
<div id="input" class="input-group col-lg-8 offset-lg-2 col-md-8 offset-md-2 col-xs-12">
<input id="search-box" type="text" class="form-control" placeholder="Search Wikipidia Pages!..." />
<button id="search" class="btn btn-primary" onclick="#">Search</button>
</div>
<div id="results" class="col-lg-8 offset-lg-2">
</div>
</div>
Insted of using $('#title').slideUp(3000) try use $('#title').animate({'margin-top': '0'}, 3000);
Then the title will remain.
Also, you might want to remove onclick="#" from <button id="search" class="btn btn-primary" onclick="#">Search</button>
Example below.
$(document).ready(function() {
//bringing focus to search box
window.load = function() {
document.getElementById("search-box").focus();
};
//listener for search button
$("#search").click(function() {
$('#title').animate({'margin-top': '0'}, 3000);
//$("#title").slideUp(3000);
// $("#title").css("text-align", "left");
search();
});
function search() {
//grabbing the id of search result div
var srchResult = document.getElementById("results");
//string entered by user for search
var searchStr = document.getElementById("search-box").value;
//replace space with _ in search query
searchStr = searchStr.replace(" ", "_");
$.ajax({
url: "https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=" + searchStr + "&prop=info&inprop=url&utf8=&format=json",
dataType: "jsonp",
success: function(response) {
if (response.query.searchinfo.totalhits === 0) {
showError(searchStr);
} else {
displayResults(response);
}
},
error: function() {
alert("Something went wrong.. <br>" +
"Try again!");
}
});
function displayResults(response) {
var search = response.query.search;
var srchLength = response.query.search.length;
srchResult.innerHTML = "";
// console.log(srchResult.innerHTML);
//pulling title and searchbox to top
// $("#title").css("margin-top:", "10px !important");
for (var i = 0; i < srchLength; i++) {
srchResult.innerHTML += '<div class="output"><h4>' + search[i].title + ' </h4><p>' + search[i].snippet + '</p></div>';
}
}
return false;
}
function showError(search) {
srchResult.innerHTML = '<div class="output text-center"><h4>No Search result for: ' + search + '</h4></div>';
}
});
body {
background-color: #495444;
}
search-input {
width: 90%;
}
center {
align-left: auto;
align-right: auto;
text-align: center;
}
.output {
background-color: white;
border-color: black;
border-width: 1px;
border-style: solid;
opacity: 0.5;
margin-top: 10px;
}
h1 {
margin-top: 200px;
color: #1484e5;
font-family: 'Josefin Sans', sans-serif;
font-size: 50px;
padding-bottom: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Josefin+Sans" rel="stylesheet">
<div class="container ">
<h1 id="title" class="text-center"><strong>WikiSearch</strong></h1>
<div id="input" class="input-group col-lg-8 offset-lg-2 col-md-8 offset-md-2 col-xs-12">
<input id="search-box" type="text" class="form-control" placeholder="Search Wikipidia Pages!..." />
<button id="search" class="btn btn-primary">Search</button>
</div>
<div id="results" class="col-lg-8 offset-lg-2">
</div>
</div>
Add this to the h1 class
h1 {
z-index: 1000;
}
Now let's say you needed something to then go on top of the header, you'd give that element's class a z-index of something higher than 1,000, so maybe 1,001! If you needed something to go behind, simply make it 999 or lower. Using 1,000 gives you a lot of free range in either direction (+/-) to work with.

Get the number of dates time later than current datetime

I have a array of objects where i have a property of date_time, and in this array i wish to get the lenght of dates_times objects that are sooner with my current date_time.
Ex:
[data:[{date: "2017-02-24 16:41:51"}, {date: ""2017-02-21 16:41:51"}...],
last_clicked: "2017-02-24 19:41:51"]
I wish to get the length of objecs on "data" array that haves a date_time sooner than "last_clicked".
Maybe you can try something like this:
let length = data.filter(function(d) {
return new Date(d.date) < new Date(last_clicked)
}).length;
You can use Angulars-orderBy-filter. I set up an example for you, just swap out where I have the id key for your data's date key.
function exampleController($scope, exampleFactory, $filter) {
$scope.list = [];
$scope.reverse = false;
$scope.changeSortOrder = function() {
$scope.reverse = !$scope.reverse;
};
function getList() {
exampleFactory
.getList()
.then(function(list) {
// $scope.list = list;
//alternatively you could filter here and change the sort order with the button if needed at all.
$scope.list = $filter('orderBy')(list, '-id'); //where this would be replaced by date(note: the '-' in front of id changes the sort order ASC/DESC)
//$scope.list = $filter('orderBy')(list, 'id', true); //where 3rd argument is wether order should be reversed
});
}
getList();
}
function exampleFactory($http) {
var root = 'http://jsonplaceholder.typicode.com';
function getList() {
return $http.get(root + '/comments')
.then(function(resp) {
return resp.data;
});
}
return {
getList: getList
};
}
angular
.module('app', [])
.controller('exampleController', exampleController)
.factory('exampleFactory', exampleFactory);
.container-fluid {
background-color: #1D1F20;
color: #fff;
font-size: 14px;
font-weight: bold;
button {
margin-top: 20%;
}
}
ul li {
list-style: none;
margin: 10px;
}
.child-padding>div {
padding: 2px;
}
.col-md-2 {
position: fixed;
button {
margin-bottom: 10%;
}
}
.btn-circle {
width: 30px;
height: 30px;
text-align: center;
padding: 6px 0;
font-size: 12px;
line-height: 1.428571429;
border-radius: 50%;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div class="container-fluid" ng-app="app">
<div class="container" ng-controller="exampleController">
<div class="row">
<div class="col-md-2 text-center">
<button class="btn btn-primary" type="button" ng-click="changeSortOrder()">Change Sort Order</button>
</div>
<div class="col-md-10 pull-right">
<ul class="ul">
<li ng-repeat="comment in list | orderBy: 'date': reverse track by $index">
<div class="child-padding">
<div>
<span ng-bind="comment.email"></span>
<span class="pull-right btn-info btn-circle" ng-bind="comment.id"></span>
</div>
<div ng-bind="comment.body"></div>
</div>
<div ng-bind="comment.name"></div>
</li>
</ul>
</div>
</div>
</div>
</div>
Use Date.parse to parse your date strings
DEMO JsFiddle
The following code will alert:
Sooner times: 1
var o = {
data:[
{date: "2017-02-24 16:41:51"},
{date: "2017-02-21 16:41:51"}
],
last_clicked: "2017-02-24 12:41:51"
};
function getSoonerLength(data, soonerThan) {
var count = 0;
for (var i = 0 ; i < data.length ; i++) {
var d = Date.parse(data[i].date);
var than = Date.parse(soonerThan);
if (d < than)
count++;
}
return count;
}
alert('Sooner times: ' + getSoonerLength(o.data, o.last_clicked))

Angular list of items ng-repeat, ng-click show description on full width

<div ng-controller = "MyController">
<ul class="items" >
<div ng-repeat="item in colors" ng-class="{active:isActive(item)}" ng-click="select(item); whattoshow=!whattoshow">
<li class="col-md-3 col-sm-3 col-lg-3 col-xs-3" >
<img class="img-responsive" ng-src="images/colors/{{item.number}}.jpg">
</li>
<div class="col-md-12 col-sm-12 col-lg-12 col-xs-12" ng-class="whattoshow && isActive(item) ? 'show' : 'hidden'}">
<h2>{{item.bio}}</h2>
</div>
</div>
</ul>
</div>
That is my HTML code, the controller uses a JSON file to go through the items, and if you click on an item you shall see the description of it. As I try to show in this poorly drawn picture (http://i.stack.imgur.com/FCvmd.png), I can make the item bio appear after the item's picture, but as every description corresponds to its own item picture it makes my display order to change. I want every items description show on click below his own row of items.
Here is my angular controller if needed.
var myApp = angular.module('ProjectAssembly', []);
myApp.controller('MyController', ['$scope', '$http', function($scope, $http) {
$http.get('data/color.json').success(function(data) {
$scope.colors = data;
});
$scope.select= function(item) {
$scope.selected = item;
};
$scope.isActive = function(item) {
return $scope.selected === item;
};
}]);
I hope you can help my case, it seemed to be easy, but I can't find the solution :/
What you need is ng-repeat-start and ng-repeat-end and split your data into rows.
See details in example:
var myApp = angular.module('ProjectAssembly', []);
myApp.controller('MyController', ['$scope', '$http',
function($scope, $http) {
//$http.get('data/color.json').success(function(data) {});
$scope.colors = [{
number: 1,
bio: '1111111111111111111111111111111111111111'
}, {
number: 2,
bio: '2222222222222222222222222222222222222222'
}, {
number: 3,
bio: '33333333333333333333333333333333333333333'
}, {
number: 4,
bio: '4444444444444444444444444444444444444444'
}, {
number: 5,
bio: '55555555555555555555555555555'
}]
$scope.colors = (function(data) {
var result = [];
angular.forEach(data, function(val, index) {
var key = Math.floor(index / 4);
if (!result[key]) {
result[key] = [];
}
result[key].push(val);
});
return result;
})($scope.colors);
$scope.select = function(item, rowIndex, columnIndex) {
if (item == $scope.selected) {
$scope.selected = null;
$scope.selectedRowIndex = null;
$scope.selectedColumnIndex = null;
return false;
}
$scope.selected = item;
$scope.selectedRowIndex = rowIndex;
$scope.selectedColumnIndex = columnIndex;
};
$scope.isActive = function(item) {
return $scope.selected === item;
};
}
]);
.item-tr {
border: 1px solid #555;
margin-top: 5px;
position: relative;
background: #555;
}
.item-tr:before {
content: ' ';
position: absolute;
border-top: 5px solid transparent;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid #555;
top: -11px;
}
.item-tr-0:before {
left: 12.5%;
}
.item-tr-1:before {
left: 37.5%;
}
.item-tr-2:before {
left: 62.5%;
}
.item-tr-3:before {
left: 87.5%;
}
.item-td {
border: 1px solid #555;
}
<link href="//cdn.bootcss.com/bootstrap/3.0.0/css/bootstrap-theme.css" rel="stylesheet">
<link href="//cdn.bootcss.com/bootstrap/3.0.0/css/bootstrap.css" rel="stylesheet">
<script src="//cdn.bootcss.com/jquery/2.2.1/jquery.js"></script>
<script src="//cdn.bootcss.com/bootstrap/3.0.0/js/bootstrap.js"></script>
<script src="//cdn.bootcss.com/angular.js/1.4.7/angular.js"></script>
<div ng-app="ProjectAssembly">
<div ng-controller="MyController">
<ul class="items">
<li ng-repeat-start="row in colors track by $index" class="row">
<div class="col-md-3 col-sm-3 col-lg-3 col-xs-3 item-td" ng-repeat="item in row" ng-class="{active:isActive(item)}" ng-click="select(item,$parent.$index,$index); whattoshow=!whattoshow">
<span>
<img class="img-responsive" ng-src="images/colors/{{item.number}}.jpg">
{{item.number}}
</span>
</div>
</li>
<li ng-repeat-end ng-show="selected && selectedRowIndex==$index" class="row item-tr item-tr-{{selectedColumnIndex}}">
<div>
<h2>{{selected.bio}}</h2>
</div>
</li>
</ul>
</div>
</div>

Categories