AngularJS - Using multiple forms - javascript

I'm pretty new to AngularJS, and in my inquisitive nature have attempted (and succeeded in most respects) in writing a news page, that allows users to comment on each article.
What I'm totally unhappy with though is my use of classic Javascript to get the story from the forms dynamically; you can see that I'm creating input fields that I am using a GetElementById on. Is there anyway to tidy this up with the two way data binding? I originally did this, but my lack of knowledge meant that the model was bound to BOTH forms; what would the the 'Angular' way around this? Primarily I can see that the addComment function is just Javascript - I know I can do this more succinctly in JQuery, but don't want to. I've got it working with a single 'comment' just fine, but can't see how I would approach multiple.
The second part is my form validation doesn't work. My guess is that my stab into it isn't great.
var app = angular.module("ngStoryTime", []);
var _stories = [
{
Id: 1,
Title: 'Man Falls Off The World!',
Body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
Date: '7 March 2015',
Images: [],
Comments: [{ Body:'LOL!', Name:'Michael', Date:'1 April 2015' }, { Body:'Tis a shame that.', Name:'William', Date:'1 April 2015' }]
},
{
Id: 2,
Title: 'Woman Eats Badger!',
Body: 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum',
Date: '8 March 2015',
Images: [],
Comments: []
}
];
app.controller('StoryController', function($scope){
$scope.pageTitle = 'Welcome to the StoryTime website!';
// Initialise the story, and blank Comment property here
this.Stories = _stories;
this.addComment = function(story){
// Is there a better way to do this??
var commentValue = document.getElementById('txtComment_' + story.Id);
var nameValue = document.getElementById('txtName_' + story.Id);
// Create the object that holds the new comment value
var myNewComment = {
Body: commentValue.value,
Name: nameValue.value,
Date: '1 May 2015'
};
// Add the comment to the array
story.Comments.push(myNewComment);
commentValue.value = '';
nameValue.value = '';
};
});
<body ng-controller='StoryController as storyCtrl'>
<h1>{{pageTitle}}</h1>
<!-- Alias the controller for use in this section -->
<div ng-repeat="story in storyCtrl.Stories">
<!-- For each Story, detail and show it -->
<h2>{{story.Title}} </h2>
<h3>{{story.Date | date:'medium' }}</h3>
<p>{{story.Body}}
<div ng-repeat="comment in story.Comments">
<h4>{{comment.Name}} - {{comment.Date | date:'medium'}} </h4>
<em>"{{comment.Body}}"</em>
</div>
<!-- Show and hide an introduction depending on if a story has a comment, or not -->
<h4 ng-show="story.Comments.length > 0">Have a Comment? There are {{story.Comments.length}} comments made so far!</h4>
<h4 ng-show="story.Comments.length == 0">Have a Comment? Be the first to comment on this excellent piece of journalism</h4>
<!-- Start of the new form that holds the story's comments, we put the story's Id on all the HtmL so we can get this later, but i'm not sure if this is actually a good idea, yet. -->
<form name="frmStory_{{story.Id}}" ng-submit="storyCtrl.addComment(story)">
Name: <br />
<input id="txtName_{{story.Id}}" required /><br />
Comment:<br/>
<textarea id="txtComment_{{story.Id}}" required></textarea>
<button ng-disabled="frmStory_{{story.Id}}.$invalid">Add My Comment</button>
</form>
<hr/>
</div>
</body>

ng-model is a key here. Every time ng-repeat iterate through given collection it will create different scope for each piece of the repeated html code respectively to given object. Using ng-model allows you to manage the data in given scope.
<form name="frmStory_{{story.Id}}" ng-submit="storyCtrl.addComment(story)">
Name: <br />
<input ng-model="story.newComment.Name" required /><br />
Comment:<br/>
<textarea ng-model="story.newComment.Body" required></textarea>
<button ng-disabled="frmStory_{{story.Id}}.$invalid">Add My Comment</button>
</form>
Secondly in your code :
this.addComment = function(story)
you are referencing to the instance of the controller, better way to do this is bind everything that has connection with view with $scope
$scope.addComment = function(story)
More about ng-model: https://docs.angularjs.org/api/ng/directive/ngModel
Understaning angular model and scope system is a huge milestone to understand the Angular way to build apps. I encourage you to start with that.

Write functions in your controller this way:
$scope.yourFunctionName = function () {
}
More info here: https://docs.angularjs.org/guide/controller
As far as the form validation failing, can you paste some error messages you are getting?

Related

function doesnt work, add to favorites E-commerce project

what I need to do is when I click on the heartBtn it turns red and add the item to an array of objects in the localstorage productsInFavArr and when I click again it should turns grey and remove the object from productsInFavArr.
the adding function is working perfectly in added the item to the arr part but the drawProductUI function supposed to check if the product exist in the productsInFacArr the btn will be red and if not its gonna be grey, so i put the drawProductUIfunction in the adding and removing function after adding and removing the item but it doesnt work till I reload the page
and the removing function remove the object from the arr after two click and still doesnt change colot of the btn
so my main problem is changing the color of the btn after adding or removing the item and the removing function remove the item after two clicks
here is the code:
// Draw Product UI
function drawProductUI(array){
var productsUI = array.map((item) => {
if (productsInFavArr) {
for (const favProd of productsInFavArr){
if (favProd.id === item.id) {
// console.log(favProd);
return `
<div class="product-item">
<div class="product-img-container">
<img src=${item.imgURL} alt="products" class="product-img" width="100%" height="150px">
</div>
<div class="product-text">
<h2 class="product-title">${item.title}</h2>
<span class="product-desc">${item.desc}</span>
<p class="product-price">USD ${item.price}</p>
</div>
<div class="product-btns">
<button class="add-to-cart">Add To Cart</button>
<button class="heart-btn heart-icon-red" onclick="removeItemFromFav( ${item.id} )"><i class="far fa-heart"></i></button>
</div>
</div>
`;
}
}}
return`
<div class="product-item">
<div class="product-img-container">
<img src=${item.imgURL} alt="products" class="product-img" width="100%" height="150px">
</div>
<div class="product-text">
<h2 class="product-title">${item.title}</h2>
<span class="product-desc">${item.desc}</span>
<p class="product-price">USD ${item.price}</p>
</div>
<div class="product-btns">
<button class="add-to-cart">Add To Cart</button>
<button class="heart-btn heart-icon" ><i class="far fa-heart"></i></button>
</div>
</div>
`
});
productsContainer.innerHTML = productsUI.join("");
}
drawProductUI(allProducts)
// add to favorites
for(let f=0; f < heartBtn.length; f++){
heartBtn[f].addEventListener("click" ,()=>{
addToFavorites(allProducts[f]);
function addToFavorites(product){
if (localStorage.getItem("userValidate") && localStorage.getItem("passValidate")) {
let productsInFavObj = localStorage.getItem("productsInFavObj");
productsInFavObj = JSON.parse(productsInFavObj);
if(productsInFavObj != null){
if(productsInFavObj[product.id] == undefined){
productsInFavObj = {
...productsInFavObj,
[product.id] : product
}
}
}else{
productsInFavObj = {
[product.id] : product
}
}
let productsInFavArr = Object.values(productsInFavObj)
localStorage.setItem("productsInFavArr" , JSON.stringify(productsInFavArr) )
localStorage.setItem("productsInFavObj" , JSON.stringify(productsInFavObj) )
}else{
window.location.href = "login.html";
}
}
drawProductUI(allProducts)
})
}
// Remove From Favorite
function removeItemFromFav(id){
for(let f=0; f < heartBtn.length; f++){
let productsInFavArr = localStorage.getItem("productsInFavArr")
if(productsInFavArr){
let items = JSON.parse(productsInFavArr);
console.log("removed item:",allProducts[f]);
let filteredItems = items.filter((item) => item.id !== id);
localStorage.setItem("productsInFavArr" , JSON.stringify(filteredItems));
localStorage.setItem("productsInFavObj" , JSON.stringify(filteredItems) )
drawProductUI(allProducts)
console.log(filteredItems);
if(filteredItems.length==0){
localStorage.removeItem("productsInFavArr")
localStorage.removeItem("productsInFavObj")
}
}
}
}
and here is an example of the products
let products = [
{
title: "Sunglasses",
imgURL: "images/Products/sunglasses.jpg",
desc: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Aut nulla adipisci fugiat pariatur recusandae repudiandae fuga molestias doloremque itaque obcaecati.",
price:80,
id: 1
},
{
title: "Laptop",
imgURL: "images/Products/laptop.jpg",
desc: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Aut nulla adipisci fugiat pariatur recusandae repudiandae fuga molestias doloremque itaque obcaecati.",
price:100,
id: 2
},
{
title: "Microphone",
imgURL: "images/Products/mic.jpg",
desc: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Aut nulla adipisci fugiat pariatur recusandae repudiandae fuga molestias doloremque itaque obcaecati.",
price:75,
id: 3
},
{
title: "Cat",
imgURL: "images/Products/cat.jpg",
desc: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Aut nulla adipisci fugiat pariatur recusandae repudiandae fuga molestias doloremque itaque obcaecati.",
price:200,
id: 4
},
]
thanks
In the drawProductUI function you are not reading local storage value, but it depends on productsInFavArr which is probably only reading the local storage value ONCE. I think you need to grab the fresh value whenever you call drawProductUI in order to reflect changes made to local storage without reloading the page.
function drawProductUI(array){
let productsInFavArr = JSON.parse(localStorage.getItem("productsInFavArr")) || []; // read the value here so it's fresh before redraw happens
var productsUI = array.map((item) => {
// rest of the function code
In the drawProductUI function you define handler for selected items:
<button class="heart-btn heart-icon-red" onclick="removeItemFromFav(${item.id})">
<i class="far fa-heart"></i>
</button>
You should do the same for the unselected button version:
<button class="heart-btn heart-icon" onclick="addToFavorites(${item.id})">
<i class="far fa-heart"></i>
</button>
this way the click handler will be attached every time you redraw the UI not only once when the js file is being read, which is the case when you define the handler as you currently do.
Having this in place you need to define the addToFavorites function at the top level, so outside of the loop, just as you define removeItemFromFav.
Since the handler takes id of the product now, you need to find the appropriate product before processing it.
You also want to redraw the UI when this is clicked so add the appropriate method to the bottom of the function.
I've attached comments indicating those changes:
function addToFavorites(id){ // this is now defined at the top level as removeItemFromFav
if (localStorage.getItem("userValidate") && localStorage.getItem("passValidate")) {
const product = allProducts.find(p => p.id == id); // this and next lines handle product lookup
if (!product) { return; }
let productsInFavObj = localStorage.getItem("productsInFavObj");
productsInFavObj = JSON.parse(productsInFavObj);
if(productsInFavObj != null){
if(productsInFavObj[id] == undefined){
productsInFavObj = {
...productsInFavObj,
[id] : product
}
}
}else{
productsInFavObj = {
[id] : product
}
}
let productsInFavArr = Object.values(productsInFavObj)
localStorage.setItem("productsInFavArr" , JSON.stringify(productsInFavArr) )
localStorage.setItem("productsInFavObj" , JSON.stringify(productsInFavObj) )
drawProductUI(allProducts); // this redraws the UI
}else{
window.location.href = "login.html";
}
}
Last thing you want to correct is the removeItemFromFav function.
Loop is unnecessary
function removeItemFromFav(id){
// loop is gone now, also console.logs are gone
let productsInFavArr = localStorage.getItem("productsInFavArr")
if(productsInFavArr){
let items = JSON.parse(productsInFavArr);
let filteredItems = items.filter((item) => item.id !== id);
localStorage.setItem("productsInFavArr" , JSON.stringify(filteredItems));
localStorage.setItem("productsInFavObj" , JSON.stringify(filteredItems) )
drawProductUI(allProducts)
if(filteredItems.length==0){
localStorage.removeItem("productsInFavArr")
localStorage.removeItem("productsInFavObj")
}
}
}

How can I make html file dynamic with angularjs

I have an html file and a JS file. The JS file contains some data that I want to present in the html file depending on a clicked link from another html file
html file:
<div class="jumbotron mb-5">
{{product.name}}
</div>
<div class="jumbotron mb-5">
{{product.price}}
</div>
<div class="jumbotron mb-5">
{{product.description}}
</div>
js file
var app = angular.module('app', [']);
app.controller('prodCtrl', function ($scope) {
$scope.courses = [
{
id: 1,
name: 'prod 1',
description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid ea fugit
tempore error, tenetur adipisci hic voluptas sapiente distinctio,.',
price: 100,
image: 'public/photos/prod1.png'
},
{
id: 2,
name: 'prod 2',
description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit.',
price: 30,
image: 'public/photos/prod2.png'
},
];
});
in another html page I have the products listed as links and I want to present them in the html page up there depending on the link I clicked
You would pass the param in the route with ui-router
It sounds like you're asking for dynamic routing. There are a few questions about this on SO, but to point you in the right direction for what you're doing:
You have to declare your routes in the app config like so:
angular.module('myApp', [
'ui.router'
])
.config(function($stateProvider) {
$stateProvider
.state({
name: 'product',
url: '/product/{id}', //{id} or :id will tell it to expect the param
templateUrl: '/products/index.html',
controller: 'ProdCtrl'
});
});
Inside your index.html file, make sure you add:
<div ng-app="myApp">
<ui-view></ui-view>
</div>
(or where you want the content to appear)
And finally, your controller:
.controller('ProdCtrl', function($scope, $stateParams) {
//get id from the url and convert it to an int to match data type in courses id
//may want to consider doing some validation
$scope.productId = parseInt($stateParams.id);
var courses = [
{
id: 1,
name: 'prod 1',
description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit.',
price: 100,
image: 'public/photos/prod1.png'
},
{
id: 2,
name: 'prod 2',
description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit.',
price: 30,
image: 'public/photos/prod2.png'
}
];
//find the matching item by id and set it to scope
$scope.product = courses.map(function(item) {
if (item.id === $scope.productId) {
return item;
}
});
});
You should now be able to go to /product/1 and see prod 1
Please note this is untested code, but should get you on the right track. I also renamed a couple things for clarity and be more aligned with common practices (e.g. myApp and ProdCtrl).
If you go through the ui-router documentation, you can also achieve this without using the id in the url and have it pass params in the background. This would mean you can only access that page via links in your app and not deep link them.

Underscore template can't access data model

I am having difficulty accessing model data with underscore, nothing is appearing on screen and I'm not getting any errors. Any help would be greatly appreciated.
Here is my templating area
<script type='text/template' id='ListContainerView'>
<p> <%= ListItemModel.id %> </p>
<p> <%= ListItemModel.network %> </p>
<p> <%= ListItemModel.created_by_id %> </p>
</script>
Collection
var ListItemCollection = Backbone.Collection.extend({
baseUrl: '/api/get_accounts',
url: '/api/get_accounts',
model: ListItemModel,
total: 0,
// Sample data
TESTDATA: {"status":"ok","posts":[{"id":"1","schedule":"2020-0417 17:00:00","utc_offset":"420","project_id":"1","network":"facebook","network_name":"TestFacebookPage","network_thumb":"https://scontent.xx.fbcdn.net/v/t1.0-9/17634406_1854330461448271_6787736791983791423_n.jpg?oh=e4c3a3573c0fc59359422cfd66a3865a&oe=598721E7","message":"Test Post 1 (just text, approved)","data":[],"customer_approved":"1","manager_approved":"1","rejection_message":"","created_at":"2020-0413 17:41:03","created_by":"admin","created_by_id":"1","created_by_name":"John Admin"},{"id":"2","schedule":"2020-0419 19:00:00","project_id":"1","network":"facebook","network_name":"TestFacebookPage","network_thumb":"https://scontent.xx.fbcdn.net/v/t1.0-9/17634406_1854330461448271_6787736791983791423_n.jpg?oh=e4c3a3573c0fc59359422cfd66a3865a&oe=598721E7","message":"Test Post 2 (text with image, approved) Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.","data":{"pictures":["https://pbs.twimg.com/media/C9T6n0UUwAAOBaU.jpg"],"image_added":"true","picture":"https://pbs.twimg.com/media/C9T6n0UUwAAOBaU.jpg","type":"photo"},"customer_approved":"1","manager_approved":"1","rejection_message":"","rejection_message_manager":"","created_at":"2020-0413 17:42:34","created_by":"admin","created_by_id":"1","created_by_name":"John Admin"},{"id":"3","schedule":"2020-0421 22:00:00","network":"facebook","network_name":"TestFacebookPage","network_thumb":"https://scontent.xx.fbcdn.net/v/t1.0-9/17634406_1854330461448271_6787736791983791423_n.jpg?oh=e4c3a3573c0fc59359422cfd66a3865a&oe=598721E7","message":"Test Post 3 (link, approved) http://www.adultswim.com/videos/rick-and-morty/","data":{"image_added":"true","pictures":["http://i.cdn.turner.com/adultswim/big/img/2015/07/17/Rick%26MortyS02_fbsearchTn.jpg"],"picture":"http://i.cdn.turner.com/adultswim/big/img/2015/07/17/Rick%26MortyS02_fbsearchTn.jpg","link":"http://www.adultswim.com/videos/rick-and-morty/","name":"Watch Rick and Morty on Adult Swim","caption":"www.adultswim.com","description":"Every episode of Rick and Morty is now on AdultSwim.com for free. Rick is a mad scientist who drags his grandson, Morty, on crazy sci-fi adventures. Their escapades often have potentially harmful consequences for their family and the rest of the world. Join Rick and Morty on AdultSwim.com as they trek through alternate dimensions, explore alien planets, and terrorize Jerry, Beth, and Summer.","domain":"www.adultswim.com","type":"link"},"customer_approved":"1","manager_approved":"1","rejection_message":"","created_at":"2020-0413 17:43:29","created_by":"admin","created_by_id":"1","created_by_name":"John Admin"},{"id":"4","schedule":"2020-0424 17:00:00","network":"facebook","network_name":"TestFacebookPage","network_thumb":"https://scontent.xx.fbcdn.net/v/t1.0-9/17634406_1854330461448271_6787736791983791423_n.jpg?oh=e4c3a3573c0fc59359422cfd66a3865a&oe=598721E7","message":"Test Post 4 (text, pending)","data":[],"customer_approved":"0","manager_approved":"1","rejection_message":"","created_at":"2020-0413 17:43:48","created_by":"admin","created_by_id":"1","created_by_name":"John Admin"},{"id":"5","schedule":"2020-0426 19:00:00","network":"facebook","network_name":"TestFacebookPage","network_thumb":"https://scontent.xx.fbcdn.net/v/t1.0-9/17634406_1854330461448271_6787736791983791423_n.jpg?oh=e4c3a3573c0fc59359422cfd66a3865a&oe=598721E7","message":"Test Post 5 (picture, pending)","data":[],"customer_approved":"0","manager_approved":"1","rejection_message":"","created_at":"2020-0413 17:44:03","created_by":"admin","created_by_id":"1","created_by_name":"John Admin"},{"id":"6","schedule":"2020-0428 21:00:00","network":"facebook","network_name":"TestFacebookPage","network_thumb":"https://scontent.xx.fbcdn.net/v/t1.0-9/17634406_1854330461448271_6787736791983791423_n.jpg?oh=e4c3a3573c0fc59359422cfd66a3865a&oe=598721E7","message":"Test Post 6 (link, pending) https://www.reddit.com/","data":{"image_added":"true","pictures":["https://b.thumbs.redditmedia.com/2Hwaff37fC4f37j-3orrbjVAOVBChqbdm_dXeIhjlNw.jpg"],"picture":"https://b.thumbs.redditmedia.com/2Hwaff37fC4f37j-3orrbjVAOVBChqbdm_dXeIhjlNw.jpg","link":"https://www.reddit.com/","name":"reddit: the front page of the internet","caption":"www.reddit.com","description":"reddit: the front page of the internet","domain":"www.reddit.com","type":"link"},"customer_approved":"0","manager_approved":"1","rejection_message":"","created_at":"2020-0413 17:44:19","created_by":"admin","created_by_id":"1","created_by_name":"John Admin"}],"total":"6"},
initialize: function(models, options) {
this.total = this.TESTDATA.total;
this.reset(this.TESTDATA.posts);
}
})
Views...
var ListContainerView = SOCIView.extend({
template: _.template($('#ListContainerView').text()),
className: 'ListContainerView'
})
var SOCIView = Backbone.View.extend({
render: function() {
if (typeof this.beforeRender === 'function') {
this.beforeRender();
}
var modelData = { };
if (this.model && this.model instanceof Backbone.Model) {
modelData = this.model.toJSON();
}
if (typeof this.template === 'function') {
this.$el.html(this.template(modelData));
}
if (typeof this.afterRender === 'function') {
this.afterRender();
}
return this;
}
})
and script inside HTML...
<script type="text/javascript">
$(document).ready(function() {
// Instantiate and render Backbone view
$('.home_body').append( new ListContainerView({
collection: new ListItemCollection(),
}).render().el)
});
</script>
The toJSON method on a Backbone model gives you a simple copy of the model attributes. In your case, this:
modelData = this.model.toJSON();
should give you an object that looks like this:
{
id: 1,
schedule: "2020-0417 17:00:00",
utc_offset: "420",
...
}
Notice that there is no ListItemModel anywhere in that object. That object would be suitable for a template that looks like this:
<p> <%= id %> </p>
<p> <%= network %> </p>
<p> <%= created_by_id %> </p>
If you really wanted the ListItemModel prefixes in the template then you'd want to say:
this.template({ ListItemModel: modelData })
when calling the template function.
Alternatively you could compile the template using the {variable: 'ListItemModel'} option to _.template and leave the template and this.template call alone. So your view would have:
template: _.template($('#ListContainerView').html(), { variable: 'ListItemModel'),
and the rest wouldn't change.
Where you define your template for the ListContainerView, I think using .text() (instead of .html()) might be causing your problem. Instead try
template: _.template($('#ListContainerView').html())
and see if it works.
Jquery's .text() ignores html markup like <span> or <strong> tags (etc), so it may be removing your templating tags <% ... %> as well, giving you just a few spaces.

Angularjs - UI Boostrap: Accordion Open and close all

i have written some code to open and close all tabs of an accordion respectively use a separate 'open' and 'close' button. How ever it requires me to dynamically add a key value pair(a Boolean value) to my json data.
What is the best practice in this situation? should i add the Boolean value as a static json element or is it OK to dynamically add values when their sole purpose is for visual structure and not relevant to actual object data.
HTML/Angular directives
<div id="app" ng-app="demoApp">
<div id="controller" ng-controller="demoAppCtrl">
<uib-accordion close-others="false">
<div class="btn-group form-group">
<button type="button" class="btn btn-warning" ng-click="toggle(true)">Open</button>
<button type="button" class="btn btn-warning" ng-click="toggle(false)">Close</button>
</div>
<uib-accordion-group is-open="hero.state" ng-click="setOpened(false)" ng-repeat="hero in heroes">
<uib-accordion-heading>
{{hero.name}}
</uib-accordion-heading>
{{hero.bio}}
</uib-accordion-group>
</uib-accordion>
</div>
</div>
Javascript/Angular
var app = angular.module('demoApp', ['ngAnimate','ui.bootstrap']);
app.controller('demoAppCtrl', function($scope) {
// This json object contain only one entry as an example
$scope.heroes = [
{'name': 'Captain America', 'team': 'Avengers', 'bio': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum vitae metus placerat, condimentum nisl et, accumsan sapien. Quisque molestie magna nulla, id malesuada sem interdum a.'}
];
$scope.addDefaultState = function(val) {
for (var i=0;i<$scope.heroes.length;i++) {
$scope.heroes[i].state = val;
}
}
$scope.addDefaultState(false);
$scope.toggle = function(status) {
$scope.heroes.forEach(function(e) {
e.state = status;
});
}
});
codepen.io - Working example (with corrections)
In my opinion the static json should not contain Boolean state value. It is okay to dynamically add values for visual presentation.
In your code the function addDefaultState is not needed. The is-open="hero.state" will take care of default state cause initially it will not find state and will consider it as false. So you change your code like below it should work too:
var app = angular.module('demoApp', ['ngAnimate','ui.bootstrap']);
app.controller('demoAppCtrl', function($scope) {
// This json object contain only one entry as an example
$scope.heroes = [
{'name': 'Captain America', 'team': 'Avengers', 'bio': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum vitae metus placerat, condimentum nisl et, accumsan sapien.'}
];
$scope.toggle = function(status) {
$scope.heroes.forEach(function(e) {
e.state = status;
});
}
});

How to create a custom <select> filter in Angular.js using 2 JSON options

I have a Q&A JSON feed with the following:
"questions": [
{
"answer": "Ea et non sunt dolore nulla commodo esse laborum ipsum minim non.",
"id": 0,
"poster": "Chelsea Vang",
"question": "Ex ex elit cupidatat ullamco labore quis cupidatat. Reprehenderit occaecat mollit ex proident aliqua. Anim minim in labore pariatur adipisicing velit dolore elit nostrud proident reprehenderit in voluptate.",
"userAsked": false
},
{
"answer": null,
"id": 1,
"poster": "Serena Randolph",
"question": "Esse occaecat anim cupidatat eu sit ad eiusmod. Et tempor deserunt ea ipsum velit irure elit qui. Ipsum qui labore laboris Lorem occaecat enim Lorem exercitation ut non duis. Sit cillum incididunt culpa ipsum.",
"userAsked": true
}
]
I want to create a custom filter that allows you to filter (by select dropdown) the results by: All Questions, "userAsked: true" a.k.a My Questions, and Answered Questions (so questions that are not 'null' in this case it would be something like !null?). I know how to create a custom filter for a single object, but can't figure out how to do it in this instance since I am trying to filter by more then one option-I can't do an ng-repeat for the select options because of this.. unless I am under the wrong impression.
My view looks something like this:
<select>
<option value="all">All Questions</option>
<option value="answered">Answered Questions</option>
<option value="mine">My Questions</option>
</select>
<ul class="list-unstyled">
<li ng-repeat="questions in qa.questions">
<strong>Question:</strong><br>
{{questions.question}}<br>
<strong>Answer:</strong><br>
{{questions.answer}}
<hr>
</li>
</ul>
Controller:
sessionControllers.controller('SessionDetailCtrl', ['$scope', '$routeParams', 'SessionFactory', 'CommentsFactory', 'QAFactory', function($scope, $routeParams, SessionFactory, CommentsFactory, QAFactory){
$scope.session = SessionFactory.get({id: $routeParams.id});
$scope.comments = CommentsFactory.get({eventId: $routeParams.id});
$scope.qa = QAFactory.get({eventId: $routeParams.id});
}]);
Please help! Thank you :)
I was able to figure it out by implementing some of what #tymeJV suggested.
View
<select ng-model="filterItem.question" ng-options="item.questionType for item in filterOptions.questions">
<option value="All Questions">All Questions</option>
<option value="Answered Questions">Answered Questions</option>
<option value="My Questions">My Questions</option>
</select>
<ul>
<li ng-repeat="questions in qa.questions | filter: myCustomFilter">
<strong>Question:</strong>
<br>{{questions.question}}<br>
<strong>Answer:</strong><br>
{{questions.answer}}
</li>
</ul>
Controller
$scope.filterOptions = {
questions: [
{questionType: 'All Questions'},
{questionType: 'Answered Questions'},
{questionType: 'My Questions'}
]
};
//Mapped to the model to filter
$scope.filterItem = {
question: $scope.filterOptions.questions[0]
};
//Custom filter - filter based on the QuestionType selected
$scope.myCustomFilter = function (data) {
if ($scope.filterItem.question.questionType === "All Questions") {
return true;
} else if ($scope.filterItem.question.questionType === 'Answered Questions') {
return data.answer != null;
} else if ($scope.filterItem.question.questionType === 'My Questions') {
return data.userAsked;
}
};
Hmm, attach a model to your select filter and try something like this:
<select ng-model="questionFilter">
<option value="all">All Questions</option>
<option value="answered">Answered Questions</option>
<option value="mine">My Questions</option>
</select>
<li ng-repeat="questions in qa.questions | filter: myCustomFilter">
And the controller:
$scope.myCustomFilter = function(item) {
if (questionFilter == "all") {
return true;
} else if (questionFilter == "answered") {
return item.answer != null;
} else if (questionFilter == "mine")
return item.userAsked;
}

Categories

Resources