Unable to dynamically filter with AngularJS - javascript

I am trying to dynamically filter in my html. When I search for CMS, I need the number on the left panel to update to '1' for both Performance and Investments. I also need the applet to display on the right panel.
(With my current code I am able to display the applets only when I have a category selected, and does not update the number on the left panel)
Image
Can anyone help me better understand what I am missing here? Any help would be much appreciated!
My data:
$scope.categories = [
{
'name': 'Performance',
'applets': ['CMS', 'Performance Snapshot']
},
{
'name' : 'Investments',
'applets' : ['Commitment Widget', 'CMS']
},
{
'name' : 'Operations',
'applets' : []
}
]
controller:
$scope.categories = categories;
$scope.chooseCategory = function(category) {
$scope.selectedCategoryApplets = category.applets;
}
html:
<div id="app">
<h1>Library</h1>
<div ng-controller="MainCtrl" class="container">
<div class="row">
<div class="col-sm-4">
<h4>Categories</h4>
<input type="text" value="searchText" ng-model="searchText" placeholder="Search Applets" />
<div ng-repeat="category in categories | filter: searchText" ng-click="chooseCategory(category)">
<div>{{category.name}}<span>{{category.applets.length}}</span></div>
</div>
</div>
</div>
<div class="col-sm-8">
<h3>Applets</h3>
<div ng-repeat="value in selectedCategoryApplets | filter: searchText">
{{value}}
</div>
</div>
</div>
</div>

You should have a different variable for displaying the filtered result.
JS:
$scope.filteredCategories = $scope.categories;
$scope.filterBySearchText = function(searchText) {
if (searchText === undefined || searchText.trim() === "") {
$scope.filteredCategories = $scope.categories;
return;
}
$scope.filteredCategories = angular.copy($scope.categories).map(cat => {
cat.applets = cat.applets.filter(
app => app.indexOf(searchText) !== -1
);
return cat;
});
};
HTML:
<div class="row">
<div class="col-sm-4">
<h4>Categories</h4>
<input type="text" value="searchText" ng-model="searchText" placeholder="Search Applets" ng-change="filterBySearchText(searchText)"/>
<div ng-repeat="category in filteredCategories" ng-click="chooseCategory(category)">
<div>{{category.name}}<span>{{category.applets.length}}</span></div>
</div>
</div>
</div>
<div class="col-sm-8">
<h3>Applets</h3>
<div ng-repeat="value in selectedCategoryApplets | filter: searchText">
{{value}}
</div>
</div>
https://stackblitz.com/edit/angularjs-nxwvce

Controller:
$scope.filter = function() {
$timeout(function() {
$scope.filteredItems = $scope.filtered.length;
}, 10);
};
$scope.sort_by = function(predicate) {
$scope.predicate = predicate;
$scope.reverse = !$scope.reverse;
};
HTML:
Input element used for filter
<div>
Filter
<i class="fa fa-filter" aria-hidden="true"></i>
</div>
<input type="text" ng-model="search" ng-change="filter()" placeholder="Filter" class="form-control" />
<div> where data is getting filtered
<div ng-repeat="item in filtered = (list | filter:search | orderBy : predicate :reverse) | startFrom:(currentPage-1)*entryLimit | limitTo:entryLimit">
......
</div>
Hope this works!!

Related

How to create dynamic onclick elements in vuejs without repeating same value

I am trying to create a form in vuejs, where a group of inputs can be append onclick. It works fine, but the problem is, All inputs return the same value. I am sharing an image here :
I am sharing my code from template :
<div class="form-group" v-for="(input,k) in data.invoice_product" :key="k">
<div class="row mb-2">
<div class="col-md-3">
<select class="form-control" v-model="data.invoice_product.product_id"
#change="getProductCost">
<option v-for="(product, i) in products" :key="i" :value="product.id">{{
product.product_name }}</option>
</select>
</div>
<div class="col-md-3">
<input type="text" class="form-control" placeholder="Quantity" v-
model="data.invoice_product.quantity" #keyup="getProductCost">
</div>
<div class="col-md-3">
<input type="text" class="form-control" placeholder="Total" v-
model="data.invoice_product.total">
</div>
<div class="col-md-3">
<span>
<i class="fa fa-minus-circle" #click="removeElement(k)" v-show="k || ( !k
&& data.invoice_product.length > 1)">Remove</i>
<i class="fa fa-plus-circle" #click="addElement(k)" v-show="k ==
data.invoice_product.length-1">Add fields</i>
</span>
</div>
</div>
</div>
from my script (I am excluding irrelevant code segments) :
export default {
data() {
return {
data : {
customer_id : '',
vat : ''
},
inputs: [{
product_id : '',
quantity : '',
total : ''
}],
input: {
product_id : '',
quantity : '',
total : ''
},
products : []
}
},
methods : {
getProductCost() {
axios.get('/api/product-cost?
product_id='+this.item.product_id+'&&quantity='+this.item.quantity,
this.data).then(response => {
this.input.total = response.data
})
},
addElement() {
this.data.invoice_product.push({
product_id : '',
quantity : '',
total : ''
})
},
removeElement (index) {
this.data.invoice_product.splice(index, 1)
},
}
Input returns null if I use "input" instead :
The problem is not providing correct data to v-model.
Here, you make an iteration, where you get "input" as an element.
<div class="form-group" v-for="(input,k) in data.invoice_product" :key="k">
But here, you are providing "data.invoice_product" instead of "input".
<select class="form-control" v-model="data.invoice_product.product_id"
#change="getProductCost">
Just change "data.invoice_product.product_id" to "input.product_id", and also do it for other inputs.
You are already looping through data.invoice_product with this
<div class="form-group" v-for="(input,k) in data.invoice_product"> .... </div>
so the v-model on your select tag should be
<select v-model="input.product_id"> .... </select>
instead of
<select v-model="data.invoice_product.product_id"> .... </select>
Similar case for your input tags for Quantity and Total.
So, the code in your template should be something like this:
<div class="form-group" v-for="(input,k) in data.invoice_product" :key="k">
<div class="row mb-2">
<div class="col-md-3">
<select class="form-control" v-model="input.product_id"
#change="getProductCost">
<option v-for="(product, i) in products" :key="i" :value="product.id">{{
product.product_name }}</option>
</select>
</div>
<div class="col-md-3">
<input type="text" class="form-control" placeholder="Quantity" v-
model="input.quantity" #keyup="getProductCost">
</div>
<div class="col-md-3">
<input type="text" class="form-control" placeholder="Total" v-
model="input.total">
</div>
<div class="col-md-3">
<span>
<i class="fa fa-minus-circle" #click="removeElement(k)" v-show="k || ( !k
&& data.invoice_product.length > 1)">Remove</i>
<i class="fa fa-plus-circle" #click="addElement(k)" v-show="k ==
data.invoice_product.length-1">Add fields</i>
</span>
</div>
</div>
</div>
[Updated]
Your scripts should be left as it was before:
export default {
data() {
return {
data : {
customer_id : '',
vat : '',
invoice_product: [{
product_id : '',
quantity : '',
total : ''
}],
},
input: {
product_id : '',
quantity : '',
total : ''
},
products : []
}
},
methods : {
addElement() {
this.data.invoice_product.push(this.input)
},
removeElement (index) {
this.data.invoice_product.splice(index, 1)
},
}

How to send Javascript array as Json combined with a html form?

I am creating a restaurant menu app that a waiter can use to input orders.
I have a Js array called itemOrderList that I am storing item names in. I want to be able to send that list of item names as Json array with customer name form input field and item price to back end to be stored in my DB. I am having issues going about doing this. What should I do? Google dev tools says "ReferenceError: itemOrderList is not defined" where I am trying to stringify the Js array.
AngularJs code
.controller('orderAddCtrl', ['$scope', '$location', 'dataService', function ($scope, $location, dataService) {
$scope.itemOrderList = [];
$scope.totalItemPrices = 0;
$scope.addOrderToList = function (item) {
console.log(item.itemName);
$scope.addPricesToTotalItemPrices(item.itemPrice);
$scope.itemOrderList.push(item.itemName);
};
$scope.addPricesToTotalItemPrices = function (price) {
console.log(price);
$scope.totalItemPrices += price ;
};
$scope.removeFromOrderToList = function (index) {
console.log(index);
$scope.itemOrderList.splice(index, 1);
};
$scope.createOrder = function (order) {
var myJson = JSON.stringify(itemOrderList);
order.orderPrice = totalItemPrices;
order.orderItems = myJson;
dataService.addOrder(order).then(function () {
$location.path('/');
});
};
Html
<form class="form-horizontal" ng-init="getItems()">
<div class="row">
<div class="col-6">
<div class="form-group">
<div>
<input ng-click="createOrder(order)" class="btn btn-success" value="Create" />
Back
</div>
</div>
</div>
<div class="col-6">
<div class="form-group">
<label class="control-label">Customer Name</label>
<div class="col-lg-10">
<input type="text" class="form-control" ng-model="order.customerName" />
</div>
</div>
</div>
</div>
<div>
<h1>Total Price: ${{totalItemPrices}}</h1>
</div>
<div class="">
<h2>Food Items</h2>
<div class="row">
<button class="btn btn-success col-3" ng-repeat="i in Items" ng-click="addOrderToList(i)">{{i.itemName}}</button>
</div>
</div>
<div class="">
<h2>Order Items</h2>
<ul>
<li ng-repeat="i in itemOrderList track by $index">
<p>{{i}}/<p>
<button ng-click="removeFromOrderToList($index)">Remove</button>
</li>
</ul>
</div>
</div>
</form>
I bet you need to specify you're using vars declared in $scope as so...
$scope.createOrder = function (order) {
var myJson = JSON.stringify($scope.itemOrderList);
order.orderPrice = $scope.totalItemPrices;
order.orderItems = myJson;
dataService.addOrder(order).then(function () {
$location.path('/');
});
};

User input not binding from $scope.$watch

Just getting started with Angular and I've spent the last 2 days trying to figure out how to bind data from a new search through a service. I had the search working before with the following code before using a service:
SearchController.js
function SearchController($scope, $http){
$scope.search = ""
$scope.getGames = function (){
return $http.get("https://igdbcom-internet-game-database-v1.p.mashape.com/games/?fields=name%2Crating%2Ccover%2Curl%2Csummary%2Cfirst_release_date&limit=50&offset=0&order=release_dates.date%3Aasc&search=" + $scope.search, {"headers": {
"x-mashape-key": "KEY",
"accept": "application/json",
}
})
.success(function(resp){
$scope.games = resp
})
.error(function(data){
console.log(data)
})
}
$scope.getGames()
};
SearchController.$inject = ['$scope', '$http']
angular
.module('app')
.controller('SearchController',SearchController)
search.html
<div class="container">
<div ng-controller="SearchController">
<div class="col-md-6 col-md-offset-4">
<h1>Search for Game</h1>
<form name="form">
<input name="search" ng-model="search" ng-change="getGames()"
ng-model-options="{debounce: 1000}" placeholder="Type Game"
minlength="3"
required="required" />
<div ng-messages="form.search.$error" ng-if="form.search.$touched">
<div ng-message="required">Please type a game to search.</div>
<div ng-message="minlength">3 characters required</div>
</div>
</form>
</div>
<div class="row fix-heights">
<div class="col-md-6" ng-repeat="game in games | filter: search" class="row-eq-height">
<br>
<div class="media">
<div class="media-left">
<img class="pull-left" src="https://res.cloudinary.com/igdb/image/upload/t_thumb/{{ game.cover.cloudinary_id }}.jpg">
</div>
<div class="media-body">
<p>Title: {{ game.name }}</p>
<p>Release Date: {{ game.first_release_date | date:'mediumDate'}}
<p>Short Description: {{ game.summary }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
So my first attempt was successful but when I tried to move the code to a service I am unable to automatically update and bind the data from the new search. I've tried to use $scope.$watch and I can see the url change in the console but the results do not populate in my search.html. Below are the new changes.
function SearchController($scope, $http, GetGameService){
$scope.search = ""
search = $scope.search
GetGameService.getGames(search)
.success(function(resp){
$scope.games = resp
console.log(resp)
})
.error(function(data){
console.log(data)
})
$scope.$watch('search', function(){
search = $scope.search
GetGameService.getGames(search)
})
};
SearchController.$inject = ['$scope', '$http', 'GetGameService']
angular
.module('app')
.controller('SearchController',SearchController)
/////////GetGameService.js
function GetGameService($http){
this.getGames = function(search) {
return $http.get("https://igdbcom-internet-game-database-v1.p.mashape.com/games/?fields=name%2Crating%2Ccover%2Curl%2Csummary%2Cfirst_release_date&limit=50&offset=0&order=release_dates.date%3Aasc&search=" + search, {"headers": {
"x-mashape-key": "KEY",
"accept": "application/json",
}
})
}
}
GetGameService.$inject = ["$http"]
angular
.module('app')
.service("GetGameService", GetGameService);
<div class="container">
<div ng-controller="SearchController">
<div class="col-md-6 col-md-offset-4">
<h1>Search for Game</h1>
<form name="form">
<input name="search" ng-model="search"
ng-model-options="{debounce: 1000}" placeholder="Type Game"
minlength="3"
required="required" />
<div ng-messages="form.search.$error" ng-if="form.search.$touched">
<div ng-message="required">Please type a game to search.</div>
<div ng-message="minlength">3 characters required</div>
</div>
</form>
</div>
<div class="row fix-heights">
<div class="col-md-6" ng-repeat="game in games | filter: search" class="row-eq-height">
<br>
<div class="media">
<div class="media-left">
<img class="pull-left" src="https://res.cloudinary.com/igdb/image/upload/t_thumb/{{ game.cover.cloudinary_id }}.jpg">
</div>
<div class="media-body">
<p>Title: {{ game.name }}</p>
<p>Release Date: {{ game.first_release_date | date:'mediumDate'}}
<p>Short Description: {{ game.summary }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
Apologies for any wrong format and many thanks for any help!
The primary error is you are missing the $scope.games assign inside your $watch
I'm not sure whether you really want to call getGames on init, or intend to use it as a function.
The controller can be reorganized to reduce code replication
function SearchController($scope, $http, GetGameService){
$scope.search = ""
// getGames(); // if you need to call on init, call here
$scope.$watch('search', function(){
getGames();
})
function getGames() {
return GetGameService.getGames($scope.search)
.then(function(resp){ // it's better to use .then than .success
$scope.games = resp
console.log(resp)
}, function(data){
console.log(data)
})
}
};

AngularJS dynamic data binding in ng-repeat

I want to call an attribute of an data-binded object dynamically based on the ng-repeat object. I have created a simple setup, can anybody solve this, if it is solvable like this?
The input should get the value of the "person.item".
For example: person.id -> 100
http://jsfiddle.net/q7gs3njj/
html
<div ng-app ng-controller="TestController">
<div ng-repeat="item in list">
<label>{{ item }}:</label>
<input type="text"/>
</div>
{{list}}
</div>
javascript
function TestController($scope) {
$scope.list = [ 'id', 'name', 'gender' ];
$person = { id:'100', name:'John', age:'22', gender:'Male' };
}
Thank you!
Of course, just use item as index:
<div ng-app ng-controller="TestController">
<div ng-repeat="item in list">
<label>{{ item }}:</label>
<input type="text" ng-model="person[item]"/>
</div>
{{list}}
</div>
And the person must be in the scope:
function TestController($scope) {
$scope.list = [ 'id', 'name', 'gender' ];
$scope.person = { id:'100', name:'John', age:'22', gender:'Male' };
}

How to filter and object based on fields values using a filter with ng-repeat

I'm using Angular and I've this object:
$scope.items = {
'abcdhx3': {name:'file1.jpg', type:'.jpg', size:30000},
'sxcdhb2': {name:'file2.jpg', type:'.png', size:30000},
'k4cdhx5': {name:'file3.jpg', type:'.jpg', size:30000},
'23cdhd3': {name:'file4.jpg', type:'.png', size:30000},
'ascdhx3': {name:'file45.jpg', type:'.png', size:30000}
};
I want to filter this object based on "name" and "type" values that I can get from input texts. So how can I do that using a filter in ng-repeat, for example: I want to show files that contains "file4" with type ".png".
<div data-ng-repeat="(key, item) in items">
<div>{{ item.name }}</div>
</div>
I've solved the problem using a transformation filter like this:
angular.module('test').filter('itemsFilter', [
function() {
return function(items) {
var list = [];
for (var i in items) {
list.push(items[i]);
}
return list;
};
}
]);
And filtering items before apply the search filter
Name: <input type="text" ng-model="search.name" />
Type: <input type="text" ng-model="search.type" />
<div ng-repeat="item in items | itemsFilter | filter:search">
<li>{{item.name}}</li>
<li>{{item.type}}</li>
</div>
You can do this by using a simple Filter:
HTML
Name: <input type="text" ng-model="search.name" />
Type: <input type="text" ng-model="search.type" />
Whatever: <input type="text" ng-model="search.$" />
<ul ng-repeat="item in items | filter:search">
<li>{{item.name}}</li>
<li>{{item.type}}</li>
</ul>

Categories

Resources