User input not binding from $scope.$watch - javascript

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)
})
}
};

Related

Unable to dynamically filter with AngularJS

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!!

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('/');
});
};

How to use multiple ng-app and add new modal

Here is my todo.js file
//let example = angular.module("example", ["ngStorage"]);
example.controller("ExampleController", function($scope, $localStorage) {
$scope.save = function() {
let testObject = [
{
name:"aaa",
lastName:"bbb"
},
{
name:"ccc",
lastName:"ddd"
}
]
let myVal = $localStorage.myKey;
$localStorage.$reset();
if(!myVal){
console.log("okey");
$localStorage.myKey = testObject;
} else {
myVal.push({
name:"fff",
lastName:"ggg"
})
$localStorage.myKey = myVal;
}
$scope.datas = $localStorage.myKey;
}
$scope.load = function() {
console.log($localStorage.myKey)
}
});*/
var app = angular.module("modalFormApp", ['ui.bootstrap']);
app.controller("modalAccountFormController", function ($scope, $modal, $log) {
$scope.showForm = function () {
$scope.message = "Show Form Button Clicked";
console.log($scope.message);
var modalInstance = $modal.open({
templateUrl: 'modal.html',
controller: ModalInstanceCtrl,
scope: $scope,
resolve: {
userForm: function () {
return $scope.userForm;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
});
var ModalInstanceCtrl = function ($scope, $modalInstance, userForm) {
$scope.form = {}
$scope.submitForm = function () {
if ($scope.form.userForm.$valid) {
console.log('user form is in scope');
$modalInstance.close('closed');
} else {
console.log('userform is not in scope');
}
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
And here is my index.html file:
<html>
<head>
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<script src="../node_modules/angular-1.6.9/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ngStorage/0.3.10/ngStorage.min.js"></script>
<script src="./todo.js"></script>
<script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.9.0.js"></script>
</head>
<body>
<!--<div ng-app="example">
<div ng-controller="ExampleController">
<button ng-click="save()">Save</button>
<button ng-click="load()">Load</button>
<br>
<input type='text' ng-model='searchText' placeholder="Search..." />
<ul>
<li ng-repeat="data in datas | filter:searchText">
{{data.name}}
</li>
</ul>
</div>
</div>-->
<div ng-app="modalFormApp">
<div class="container">
<div class="col-sm-8 col-sm-offset-2">
<!-- PAGE HEADER -->
<div class="page-header">
<h1>AngularJS Form Validation</h1>
</div>
<div ng-controller="modalAccountFormController">
<div class="page-body">
<button class="btn btn-primary" ng-click="showForm()">Create Account</button>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Lastly here is my modal.html:
<div class="modal-header">
<h3>Create A New Account!</h3>
</div>
<form name="form.userForm" ng-submit="submitForm()" novalidate>
<div class="modal-body">
<!-- NAME -->
<div class="form-group">
<label>Name</label>
<input type="text" name="name" class="form-control" ng-model="name" required>
<p ng-show="form.userForm.name.$invalid && !form.userForm.name.$pristine" class="help-block">You name is required.</p>
</div>
<!-- USERNAME -->
<div class="form-group">
<label>Username</label>
<input type="text" name="username" class="form-control" ng-model="user.username" ng-minlength="3" ng-maxlength="8" required>
<p ng-show="form.userForm.username.$error.minlength" class="help-block">Username is too short.</p>
<p ng-show="form.userForm.username.$error.maxlength" class="help-block">Username is too long.</p>
</div>
<!-- EMAIL -->
<div class="form-group">
<label>Email</label>
<input type="email" name="email" class="form-control" ng-model="email" required>
<p ng-show="form.userForm.email.$invalid && !form.userForm.email.$pristine" class="help-block">Enter a valid email.</p>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" ng-disabled="form.userForm.$invalid">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</form>
I'm trying to open a modal when i click the button. I made comment line the other part which i'm using but it works fine. The second part is for only the modal but it is not working. I even can not open the modal. If there is a basic way to do this can you share with me? I only need to open this modal. I can handle the rest of it.
From the Docs:
There are a few things to keep in mind when using ngApp:
only one AngularJS application can be auto-bootstrapped per HTML document. The first ngApp found in the document will be used to define the root element to auto-bootstrap as an application. To run multiple applications in an HTML document you must manually bootstrap them using angular.bootstrap instead.
For more information, see
AngularJS ng-app Directive API Reference

TypeError: Cannot read property 'field1' of undefined?

I want to replicate template in angularjs. like when I click on add button it add new form, when I click on remove button it remove form. When I submit button it send data to backend, but it is giving error
TypeError: Cannot read property 'field1' of undefined, why It is not able to take $scope.field.field1 value.
Please see demo, it is not working
HTML
<div ng-app="myApp" ng-controller="myCtrl">
<div class="col-md-12" style="bottom:10px" >
<div class="form-group" ng-repeat="field in fields">
<div class="col-md-12">
<div class="col-md-4">
<label class="col-md-12 control-label">Field1</label>
<div class="col-md-12">
<input data-ng-model='field.field1' class="chosen-select input-md form-control sme-input-box"/>
</div>
</div>
<div class="col-md-4">
<label class="col-md-12 control-label">Field2</label>
<div class="col-md-12">
<input ng-model='field.field2' class="chosen-select input-md form-control sme-input-box"/>
</div>
</div>
</div>
<div class="col-md-12">
<div class="col-md-3">
<a class="btn btn-success" ng-click="removeTemplate($index)">Remove</a>
</div>
<div class="col-md-3">
<a class="btn btn-success" ng-click="updateOrder()">Submit</a>
</div>
</div>
</div>
<div class="col-md-3" style="top:5px">
<a class="btn btn-success" ng-click="cloneTemplate()">Add</a>
</div>
</div>
</div>
Angularjs
$scope.fields=[
{
"field1": "",
"field2": "",
}
]
// update and get invoice details
$scope.cloneTemplate=function(){
var clone_template={ "field1": "", "field2": ""};
$scope.fields.push(clone_template);
}
$scope.removeTemplate= function(templateIndex){
$scope.fields.splice(templateIndex,1);
}
$scope.updateOrder=function(){
var updateOrder={
"field1":$scope.field.field1,
"field2":$scope.field.field2,
}
$http.post(config.server, updateOrder)
.success(function(response, status){
console.log(response);
})
.error(function(response, status){
console.log(response);
})
}
There is no $scope.field, read about Understanding Scopes.
You can pass the $index in your updateOrder function in the view
<a class="btn btn-success" ng-click="updateOrder($index)">Submit</a>
and use it like this
$scope.updateOrder=function(i){
var updateOrder = {
"field1":$scope.fields[i].field1,
"field2":$scope.fields[i].field2,
};
.....
};
Working Demo
You had wrong variable name inside ng-repeat, It should be list1 instead of `fields.
Markup
<div class="form-group" ng-repeat="field in list1">
Either way you could rename the scope variable property to $scope.fields instead of $scope.list1
Edit
On submit method you should pass the whole field object to updateOrder method, and directly pass that object to $http call.
Addtionally you need to add missing $http dependency on your controller function.
Markup
<a class="btn btn-success" ng-click="updateOrder(field)">Submit</a>
Code
$scope.updateOrder = function(field) {
//pass the field object as is, because it will have field1 & field2
var config = { server: '/url'}//should be something
$http.post(config.server, field)
.success(function(response, status) {
console.log(response);
})
.error(function(response, status) {
console.log(response);
})
}
Demo Here

Angular ngResource's $save() not working

I want to post data to my asp.net webapi controller, by using $save() method that belong to ngResource i am getting this error:
"TypeError: $scope.product.$save is not a function at n.$scope.saveProduct "
when i used $http(), data is getting saved but $save() is giving me error, other methods like $query() and $get() are working properly only $save() is causing an error.
code:
// first file (module)
var app = angular.module('commonServices', ['ngResource'])
.constant('appSettings', {
serverPath: 'http://localhost:29904/'
});
//second file (factory)
(function(){
angular.module('commonServices')
.factory('productResource', ['$resource', 'appSettings', productResource])
function productResource($resource, appSettings) {
return $resource(appSettings.serverPath + "api/Products/:id",
null,
{
'update': { method: 'PUT' },
});
}
}());
// third file (controller)
myApp.controller('editProductController', ['$scope', '$routeParams', '$http', 'productResource',
function ($scope, $routeParams, $http, productResource) {
$scope.num = $routeParams.id;
$scope.alertUser = false;
$scope.saveProduct = function () {
$scope.product.$save(function(data){});
}
};
}]);
// some markup from template
<div class="form-group ">
<label class="col-md-2 control-label"
for="inputProductName">Product Name</label>
<div class="col-md-4">
<input class="form-control"
id="inputProductName"
name="inputProductName"
type="text"
placeholder="Product Name (required)"
required
ng-model="product.productName" />
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="inputProductCode">Product Code</label>
<div class="col-md-4">
<input class="form-control"
id="inputProductCode"
name="inputProductCode"
type="text" ng-model="product.productCode">
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label"
for="inputAvailabilityDate">Availability</label>
<div class="col-md-4">
<div class="form-control">
{{product.releaseDate}}
</div>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label"
for="inputDescription">Description</label>
<div class="col-md-4">
<textarea class="form-control"
id="inputDescription"
name="inputDescription"
placeholder="Description"
rows="3" ng-model="product.description"></textarea>
</div>
<br />
</div>
<div class="form-group">
<div class="col-md-4 col-md-offset-2">
<span>
<button class="btn btn-primary"
style="width:80px;margin-right:10px" ng-click="saveProduct()">
Save
</button>
</span>
to use $save() without calling get what i do is here:
productResource.save($scope.product, function(data) {
});
Thanks #TzachOvadia for providing me a clue :)
Try this:
$scope.product = productResource.get({ id: $scope.num });
$scope.saveProduct = function () {
$scope.product.$save(function (response) {...});
}

Categories

Resources