Acessing a Variable outside ng-repeat - angularjs

Could someone help me with this question.
I am trying to understand how can I access the variable "new_name_input" inside one of my functions. The goal it's just to set my variable with the name choosen. I would be glad if someone could explain it to me or/and show me how can I make it.
See code below.
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl',['$scope',function($scope){
$scope.names=
[
{nome:'Renan'},
{nome:'Geraldo'}
];
$scope.newNames=
[
{nome:'Fernando'},
{nome:'José'}
];
$scope.transfer=function(novoNome,place){
alert(novoNome.nome);
this.place = novoNome.nome;
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
<div style='border:1px solid black;display:inline;margin:10px 10px 10px 10px'
ng-repeat='name in names'>
<input type='text' ng-model='new_name_input'/>
<div>
<p ng-click='transfer(new_name,new_name_input)' ng-repeat='new_name in newNames'>
{{new_name.nome}}
</p>
</div>
</div>
</body>
When I click on the names written, the names does not get added in the textbox. I am wondering how can I do it without remove the ng-repeat from the code

Not sure I'm following your question completely, but is this what you're attempting to do? Basically, you need a "." in the ng-model in order to have two-way bindings work with primitives. I used ng-init to create a new object to achieve that, but I wonder if you mean to get the new value back into the name object? If so, you can drop the ng-init and just pass the name object into the transfer method instead.
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl',['$scope',function($scope){
$scope.names=
[
{nome:'Renan'},
{nome:'Geraldo'}
];
$scope.newNames=
[
{nome:'Fernando'},
{nome:'José'}
];
$scope.transfer=function(novoNome,nameVm){
alert(novoNome.nome);
nameVm.new_name_input= novoNome.nome;
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
<div style='border:1px solid black;display:inline;margin:10px 10px 10px 10px'
ng-repeat='name in names' ng-init="nameVm = {}">
<input type='text' ng-model='nameVm.new_name_input'/>
<div>
<p ng-click='transfer(new_name,nameVm)' ng-repeat='new_name in newNames'>
{{new_name.nome}}
</p>
</div>
</div>
</body>

Related

How do you change all ng-show dependent variables in an ng-repeat?

I have an ng-repeat that has an ng-if attached to it, with a child element that I am changing with an ng-click. The code looks something like the following:
<div ng-repeat="object in objects" ng-if="show">
<div ng-click="show = !show">Show</div>
</div>
Lets say I had 2 objects, it would load two repeated divs, and there would be two 'show' elements. When I click show, it will only remove one of the repeated elements from the page. I need it to remove both. Thoughts?
If you want to hide all I would wrap all of it in an outer div and place the "ng-if" there.
<div ng-if="show">
<div ng-repeat="object in object">
<div ng-click="show = !show">Show</div>
</div>
</div>
I would however advise to place any logic that modifies data in the TS file instead of in the html view.
Your template is almost correct, the only thing that is worth mentioning is that:
The scope created within ngIf inherits from its parent scope
using prototypal inheritance.
The main caveat of prototypal inheritance is that setting a primitive value on the child scope shadows the value on the parent scope. There are different approaches of how to avoid this, see the code snippet below:
angular.module('app', [])
.controller('mainController', function mainController($scope) {
var ctrl = this;
$scope.show = true;
$scope.showList = {value: true};
$scope.objects = [{}, {}, {}];
$scope.toggleShowVar = function(){
$scope.show = !$scope.show;
};
ctrl.show = true;
});
<!-- JS -->
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.js"></script>
<body ng-app="app">
<div class="container" ng-controller="mainController as $mainCtrl">
<p>This will not work due to scope prototypal inheritance:</p>
<div ng-if="show">
<div ng-repeat="object in objects">
<div ng-click="show = !show">Show {{show}}</div>
</div>
</div>
<p>Using "controller as" will help us:</p>
<div>
<div ng-repeat="object in objects" ng-if="$mainCtrl.show">
<div ng-click="$mainCtrl.show = !$mainCtrl.show">Show {{$mainCtrl.show}}</div>
</div>
</div>
<p>Or simply using "dot in the model":</p>
<div>
<div ng-repeat="object in objects" ng-if="showList.value">
<div ng-click="showList.value = !showList.value">Show {{showList.value}}</div>
</div>
</div>
<p>Or using controller method for toggle:</p>
<div>
<div ng-repeat="object in objects" ng-if="show">
<div ng-click="toggleShowVar()">Show {{show}}</div>
</div>
</div>
<p>Or using $parent to change it in the controller's scope:</p>
<div>
<div ng-repeat="object in objects" ng-if="$parent.show">
<div ng-click="$parent.$parent.show = !$parent.$parent.show ">Show {{$parent.show}}</div>
</div>
</div>
</div>
</body>

get dynamic input value using ng-model in angularjs

I have list of product and from each product use will add some quantity which I need in my controller.
the product list is not fix it might be 10 product (10 input box for qty) or may 100 product.
In HTML this is my code
<div class="col m3" ng-repeat="product in productlist" ng-cloak>
<h6 style="font-size:14px;">{{ product.product_name }}</h6>
<input ng-modal="qty[product.pid]" placeholder="Qty" type="number">
<button class="btn-flat" ng-click="addQty(product.pid)">ADD</button>
</div>
I am not understanding how I can get this value along with pid in controller
// ORDER ADD
$scope.addQty = function (pid) {
//$scope.qty = {};
//$scope.product = $scope.productlist;
console.log($scope.qty[pid]);
var getlistURL = $scope.baseURL+$scope.uri.uri_1+"/"+$scope.uri.uri_2+"/ng_add_to_cart/"+pid+"/";
$http.post(getlistURL).
success(function(data, status) {
if(data.length != 0) {
$scope.stockmovement = data;
$('#showStockMovement').modal('open');
}
});
};
Thanks
Here you should not try to pass just id like you did in the code. Instead of that, pass the entire object to the function and when you will manipulate its attributes, it will get effect on the page at the same time. This is how you can get real time effect of quantity change.
So modify the argument of the addQty function.
<div class="col m3" ng-repeat="product in productlist" ng-cloak>
<h6 style="font-size:14px;">{{ product.product_name }}</h6>
<input ng-modal="qty[product.pid]" placeholder="Qty" type="number">
<button class="btn-flat" ng-click="addQty(product)">ADD</button>
</div>
Then you can access any attribute of the product object in the function. Like written below:
// ORDER ADD
$scope.addQty = function (product) {
//$scope.qty = {};
//$scope.product = $scope.productlist;
console.log(product.pid);
var getlistURL = $scope.baseURL+$scope.uri.uri_1+"/"+$scope.uri.uri_2+"/ng_add_to_cart/"+product.pid+"/";
$http.post(getlistURL).
success(function(data, status) {
if(data.length != 0) {
$scope.stockmovement = data;
$('#showStockMovement').modal('open');
}
});
};
This is your solution.
Deducing from the limited information that is available, my guess is that you haven't initialized your array qty.
You should initialize your qty array to a length equal to the length of your productlist, with all initial values set to 0.
$scope.qty = [];
var initQty = function(){
for(var i = 0;i<$scope.productlist.length;i++){
$scope.qty[i]=0;
}
}();
Apart from that, your code looks fine after changing the ng-modal to ng-model, as already suggested by other folks.
I have updated my answer
angular.module('myApp', [])
.controller('myController', function($scope) {
//$scope.productlist = [];
$scope.modelData = {};
$scope.productlist = {
0:{"pid" : 1,
"product_name" : "Laptop",
},
1:{"pid" : 2,
"product_name" : "Computer",
},
2:{"pid" : 3,
"product_name" : "Camera",
},
3:{"pid" : 4,
"product_name" : "Tv",
}
}
$scope.addQty = function(pid){
alert(pid)
console.log($scope.modelData[pid])
}
});
<html ng-app="myApp">
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.18/angular.js"></script>
<script type="text/javascript" src="app.js"></script>
<title>Angular-Google-Charts Example</title>
</head>
<body>
<div class="modal" style="background-color: #EDEEF1;" ng-controller="myController">
<div class="col m3" ng-repeat="product in productlist" ng-cloak>
<h6 style="font-size:14px;">{{ product.product_name }}</h6>
<input ng-model="modelData[product.pid]" placeholder="Qty" type="number">
<button class="btn-flat" ng-click="addQty(product.pid)">ADD</button>
</div>
</div>
</body>
</html>
Please check this demo. Let me know if it works!
if your productlist is of this type.
$scope.productlist =
[
{product_name: "Soap", pid: 25},
{product_name: "Bag", pid: 100}
];
Use this code
<div class="col m3" ng-repeat="product in productlist">
{{ product.product_name }}
<input ng-model="product.pid" placeholder="Qty" type="number">
<button class="btn-flat" ng-click="addQty(product)">ADD</button>
</div>
if you want to enter pid manually and get value in controller and don't want to attach with productlist
<div class="col m3" ng-repeat="product in productlist">
{{ product.product_name }}
<input ng-model="qty" placeholder="Qty" type="number">
<button class="btn-flat" ng-click="addQty(qty)">ADD</button>
</div>
and in your code type error is there change "ng-modal" to "ng-model"
seems like you want only the qty number so better try second one
https://plnkr.co/edit/gBLx1iIWL4x84ldtQAtF?p=preview
Here is a working plunker.. Changes I made are only 2 ng-model instead of ng-modal and initialised qty as object so now your quantities will be stored with pid as key of the object and qty as value of the object
for example :-
{ "1" : 6, "2" :12}
so you can basically have as many products you want with this
ng-repeat is an isolated scope , so model variables created in ng-repeat are not accessible in controller.
To solve your requirement we can follow two approaches
1.you can pass product id and quantity as two arguments and retrieve that in a function
<body ng-app="myApp" ng-controller="myCtrl">
<div class="col m3" ng-repeat="product in productlist" ng-cloak>
<h6 style="font-size:14px;">{{ product.product_name }}</h6>
<input ng-model="qty" placeholder="Qty" type="number">
<button class="btn-flat" ng-click="addQty(product.id,qty)">ADD</button>
</div>`
but this approach we dont have track of all products and all quantities, please check below plunker
https://plnkr.co/edit/zAz6FVgazOH4NWYpKb4u?p=preview
2.Second approch is following ControllerAs concepts , instead of using $scope we use ControllerAs to get values inside controller model.
<body ng-app="myApp" ng-controller="myCtrl as ctl">
<div class="col m3" ng-repeat="product in ctl.productlist" ng-cloak>
<h6 style="font-size:14px;">{{ product.product_name }}</h6>
<input ng-model="ctl.qty[product.id]" placeholder="Qty" type="number">
<button class="btn-flat" ng-click="ctl.addQty(product.id)">ADD</button>
</div>
{{ctl.qty}}
In this approach we can build an object called qty where we can keep track of all products and quantities
Please check below plunker for this approach
https://plnkr.co/edit/QIVGt8MCdPRAhHaqVA64?p=preview
Hope this meets your requirement

Angular Wiring up two controllers from Model to auto fill form

I've got an angular app I'm working on where I'm trying to auto fill a pop up modal based on a user's selection.
I thought I could use my model service to keep track of what the user selected and 'wire' the controller for the <select> list and it's edit button to the model but that doesn't seem to work.
Adding to the complexity I'm using angular-route and my <select> list is buried in a view. I was trying to keep my pop up modals in a separate controller outside the view because they've got their own templates and I had problems when I nested them into the view...
I've seen a few examples of wiring up angular apps and thought I understood them but I can't figure out what I'm doing wrong.
EDIT (thanks Pankaj Parkar for pointed out my mistakes in the plunker):
I have a plunker here:
https://plnkr.co/edit/6f9FZmV8Ul6LZDm9rcg9?p=preview
Below is the snipped in a single HTML page with CDN links :).
Am I just completely misunderstanding how angularjs is suppose to work?
<html ng-app="myApp">
<head>
<title>Bootstrap 3</title>
</head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<body>
<div ng-view></div>
<script id="editables.html" type="text/ng-template">
<div class="container">
<div class="jumbotron">
<form>
<div class="form-group">
<select class="form-control" id="mapsSelect" size="10" multiple ng-model="model.selected">
<option ng-repeat="n in editables">{{n}}</option>
<select>
</div>
<a href="#editModal" class = "btn btn-info" data-toggle="modal" ng-click="edit()" >Edit</a>
</form>
</div>
</div><!--end container div-->
</script>
<div ng-controller="modalsController">
<div id="editModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<form class="form-horizontal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4>New Map</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="name" class="col-lg-3 control-label">Name</label>
<div class="col-lg-9">
<input type="text" class="form-control" id="name" ng-model="formModel.name"></input>
</div>
</div>
<div class="form-group">
<label for="desc" class="col-lg-3 control-label">Description</label>
<div class="col-lg-9">
<input type="text" class="form-control" id="desc" ng-model="formModel.desc"></input>
</div>
</div>
<div class="modal-footer">
<pre> {{ formModel | json }}<br><br>Working: {{ workingMap }}</pre>
Cancel
Continue
</div>
</form>
</div>
</div>
</div><!-- end modal -->
</div>
</body>
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src = "https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular-route.min.js"></script>
<script src = "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- <script src = "js/script.js"></script> -->
<script>
var app = angular.module('myApp', ['ngRoute']);
var modelService = function ($log){
var moduleHello = function(myMessage){
console.log("Module hellow from myService " + myMessage);
}
var moduleNames = {
"First" : {desc: "First's Description"},
"Second" : {desc: "Second's Description"},
"Third" : {desc: "Third's Description"}
};
var moduleWorkingName = {};
return {
hello: moduleHello,
editables: moduleNames,
workingName: moduleWorkingName
}
}//end modelService
app.factory("modelService", ["$log", modelService]);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/editables', {
controller: "editablesController",
templateUrl: "editables.html"
}).
otherwise({
redirectTo: "/editables"
});
}]);
app.controller('editablesController', ['$scope', '$log','modelService', function($scope,$log, $modelService) {
$scope.model = {};
//console.log( JSON.stringify( $modelService.editables ) );
$scope.editables = [];
for ( name in $modelService.editables){
$scope.editables.push( name );
}
$scope.edit = function(){
if ( typeof $modelService.editables [$scope.model.selected] != 'undefined'){
$modelService.workingName = $modelService.editables [$scope.model.selected];
console.log ("Setting my working name to " + JSON.stringify( $modelService.workingName ) );
}else{
console.log ("Nothing Selected");
}
}
}]);
app.controller('modalsController', ['$scope','modelService', function($scope,$modelService) {
$scope.formModel = {};
$scope.formModel.name = "Hard coding works of course";
$scope.formModel.desc = $modelService.workingName.desc; //But I can't seem to get this to update. I thought pointing it at an object in the Model would be enough.
console.log("Firing up modalsController");
}]);
</script>
</html>
I spent the last two days mulling over this in my head and I think I figured it out. For starters, here's the (working) plunker:
https://plnkr.co/edit/Kt3rebPtvGTt0WMXkQW4?p=preview
Now, the explanation. I was trying to keep a separate 'formModel' object that kept track of the controller's state. But that's both silly and pointless.
Instead what you're supposed to do is:
a. Create an object in your service to hold all your data (I just called this 'model')
b. For each controller that needs to share data create a variable on the $scope of the controller and point it to your 'model' variable from your service.
c. after that use the variables from your model in your html.
So in both my controllers you'll find this line:
$scope.model = $modelService.model;
and in my HTML you'll find stuff like this:
<input type="text" class="form-control" id="name" ng-model="model.workingName.name"></input>
notice how I'm using "model.workingName.name"? This references $scope.model.workingName.name, which thanks to the line $scope.model = $modelService.model from my JavaScript now points directly to my model.
And that is how you "wire up" Angular.
By the way, experienced Angular folks have probably noticed that this part:
$scope.editables = [];
for ( name in $modelService.model.names){
$scope.editables.push( name );
}
probably belongs in a directive instead of a controller because I'm editing the DOM.
Stuff like that's what makes it so hard to learn AngularJS. There's so many concepts to get the hang of.

ng-class not loading class dynamically

Worked in Angular js small app, using ng-class, dynamically changing the class on checking the check box.
To be more specific, I want to change the background-color of the checkbox, on checking in and off.
Below is my code snippet :
<style>
.unchecked{
background-color: red;
color:yellow;
}
.checked{
background-color: green;
color:yellow;
}
</style>
<script type="text/javascript">
var myController = function($scope){
console.log("Controller works fine");
$scope.countries=[
{'name':'India','capital':'Delhi','ischecked':'false'},
{'name':'Bangladesh','capital':'Dhaka','ischecked':'false'},
{'name':'Sri Lanka','capital':'Colombo','ischecked':'false'},
{'name':'Pakistan','capital':'Islamabad','ischecked':'false'}
];
}
</script>
<body ng-app="" ng-controller="myController">
<div ng-repeat="country in countries" class="unchecked" ng-class="{checked:country.ischecked}">
<input type="checkbox" id="{{country.name}}" name="{{country.name}}" ng-model="country.ischecked">{{country.name}}
{{country.ischecked}}
</div>
</body>
Use ischecked as boolean instead of string.
'ischecked':false
PLUNKR

Generate dynamic form input fields and collect field data in an array

I am stuck with this little task.
I need to generate form input fields dynamically by clicking 'add' button on the form.
The form is supposed to create DB table schema. So every input field is a DB table field name.
I am OK generating the fields dynamically but have trouble with gathering the actual data.
<form ng-controller="NewTableCtrl" ng-submit="submitTable()">
<input type='text' ng-model='table.title' placeholder='Title:'>
<input ng-repeat="field in fields" type='text' ng-model='table.fields' placeholder='Field:'>
<div>
<button type='submit'>Submit</button>
<button ng-click="addFormField()">Add</button>
</div>
</form>
.. and the controller
.controller('NewTableCtrl', function($scope) {
$scope.fields = [];
$scope.table = {};
$scope.addFormField = function () {
$scope.fields.push({});
}
$scope.submitTable = function () {
console.log($scope.table);
}
});
Looks simple. When I click 'Add' button it generates the new input field but it does it with the same model object (obveously). And that's where my misunderstanding lies. I thought that if I declare $scope.fields = [];in the controller then repeating field data will just go into the array. But it just echoes the input in every repeating input field. I understand now that this is how it is supposed to be with two way binding.
The reason I thought like this is by the analogy with an ordinary form submission where the repeating input field names become an array in the URL encoded form data.
So how do I solve this? The server needs to get an array of fields like this: fields: [field1, field2 ...] Do I need to generate input fields with different scope variable for each field? How do I do this?
Is this more complex then I thought and it needs to be a directive? If yes, please, show me how to do this.
Thanks.
Right now you are iterating $scope.fields. When you are adding a new field you push an empty object into $scope.fields, but every input's ng-model points to $scope.table.fields (which is non-existing until first input writes to it - then it will hold a string variable).
For this simple use case you could try:
app.controller('NewTableCtrl', function($scope) {
$scope.table = { fields: [] };
$scope.addFormField = function() {
$scope.table.fields.push('');
}
$scope.submitTable = function() {
console.log($scope.table);
}
});
And:
<input ng-repeat="field in table.fields track by $index" type='text' ng-model='table.fields[$index]' placeholder='Field:'>
Demo: http://plnkr.co/edit/6iZSIBa9S1G95pIMBRBu?p=preview
Take a look at this
Working Demo
html
<body>
<div ng-app=''>
<div ng-controller="questionCtrl">
<div>
<ul>
<li ng-repeat="elemnt in questionelemnt">
<div>
<div id={{elemnt.id}} style="display:inline" >
<span ng-model="elemnt.question" ng-hide="editorEnabled" ng-click="editorEnabled=true">
{{elemnt.question}}
</span>
<div ng-show="editorEnabled">
<input ng-model="elemnt.question" ng-show="editorEnabled" >
<button href="#" ng-click="editorEnabled=false">Done editing</button>
</div>
</div>
<div style="display:inline">
<span>
<input type="text" ng-model="elemnt.answer" placeholder="Answer" required/>
</span>
</div>
<span ng-hide="elemnt.length == 1">
<button ng-click="questionelemnt.splice($index, 1)">Remove</button>
</span>
</div>
<hr/>
</li>
<li>
<button ng-click="addFormField($event)">Add</button>
</li>
</ul>
</div>
<div>
<button ng-click="showitems($event)">Submit</button>
</div>
<div id="displayitems" style="visibility:hidden;">
{{questionelemnt}}
</div>
</div>
</div>
</body>
script
function questionCtrl($scope) {
var counter = 0;
$scope.questionelemnt = [{
id: counter,
question: 'Question-Click on me to edit!',
answer: ''
}];
$scope.addFormField = function ($event) {
counter++;
$scope.questionelemnt.push({
id: counter,
question: 'Question-Click on me to edit!',
answer: ''
});
$event.preventDefault();
}
$scope.showitems = function ($event) {
$('#displayitems').css('visibility', 'none');
}
}
Variation of tasseKATTs solution using a hashmap instead of an array.
This allows me to have a nice JSON object I can just for-in over in order to build my query filter.
http://plnkr.co/edit/CArP3Lkmn7T5PEPdXgNt?p=preview
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#*" data-semver="1.3.0" src="//code.angularjs.org/1.3.0/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<style>
div{ margin: 1em;}
input{margin-left:1em;}
</style>
</head>
<body ng-controller="myCtrl">
<h2>Using a filter map tied to ng-model to create a filter object</h2>
<div ng-repeat="field in fields">
{{field}}<input ng-model=filters[field] />
</div>
<hr>
<h3>Filter</h3>
{{filters}}
<script>
var app=angular.module("app",[]);
app.controller("myCtrl",function($scope){
$scope.filters={};
$scope.fields=["name","address","phone","state"];
});
angular.bootstrap(document,["app"]);
</script>
</body>
</html>

Resources