ng-repeat scope issue: two level inheritance? - javascript

I got an array of objects, these objects having properties.
I want to be able modify the properties of one of these objects thanks to a ng-repeat -> and that the changes I make are also done in the original array
I've tried to properly do format my array so that the inheritance is ok: when I change in the ng-repeat it changes in the $scope. But it doesn't go to the original array.
I don't know how I could change my data/arrays in order to make it work.
What should I do ? How can I transform my data ?
here is a fiddle that shows all this
ps: I've tried using the parent scope but as you can see in my example I don't even success in that :( anyway it's advised to avoid it if possible
and here's the JS code:
var myApp = angular.module('myApp',[]);
myApp.controller('myController', function ($scope) {
//source array
var origine = [
{id:"1",name:"elem1"},
{id:"2",name:"elem2"},
{id:"3",name:"elem3"} ];
//what I am doing now
$scope.element = origine[1];
//i'd like to transform the array so that I have inheritance and I can avoid using parent scope
var reference = [];
var i =0;
for (var key in origine[2]) {
if (origine[1].hasOwnProperty(key)) {
reference[i] = {prop:origine[2][key]};
i++;
}
}
$scope.element2 = reference;
$scope.element3 = origine[0];
$scope.check = function()
{
//to check if the source array is modified
console.log(origine);
}
});
and here HTML:
<div ng-app="myApp">
<div ng-controller="myController">
--------------- BASIC example -----------------
<br/> Repeat scope
<div ng-repeat="property in element">
<input ng-model="property"/>
</div>
<br/>"Parent"
<input ng-model="element.name"/>
<br/><input type="button" ng-click="check()" value="check origine"/>
<br/><br/>--------------- Trying inheritance -----------------
<br/> Repeat scope
<div ng-repeat="property in element2">
<input ng-model="property.prop"/>
</div>
<br/> "Parent"
<input ng-model="element2[1].prop"/>
<br/><input type="button" ng-click="check()" value="check origine"/>
<br/><br/>--------------- Trying parent scope -----------------
<br/> Repeat scope
<div ng-repeat="property in element3">
<input ng-model="$parent.property"/>
</div>
<br/> "Parent"
<input ng-model="element3.name"/>
<br/><input type="button" ng-click="check()" value="check origine"/>
</div>
</div>

Thats because the variable is being assigned by reference.
Try assigning the array directly to the scope and manipulate that array, or create a scope method to push, splice, update that private array

If I understand you question correctly you can refer to property by key to get it updated:
<div ng-repeat="(key, property) in element">
<input ng-model="element[key]"/>
</div>
Fiddle: http://jsfiddle.net/xezbs8gn/7/

Related

$event not defined in ng-change on text input

I hope my issue is simple to solve, I can't access the value by ng-model because i have multiple of these boxes as they are rendered as part of a list of inputs in a form. I am trying to get the ID and text value of a text box with ng-change. heres my html:
<input type="text" class="other-box" ng-model="test" id="4" ng-change="otherBoxUpdate(this)"/>
(ng-model is required, which is why it's in there). Hers is my controller snippet:
$scope.otherBoxUpdate = function (obj, $event) {
console.log(obj)
console.log($event)
console.log($event.target)
}
obj seems to return a scope value, however from what i've read I need to access $event.target, however $event is not defined. What am i doing wrong?
ng-change not allow to pass $event as parameter.
ng-change="otherBoxUpdate(test)"
var myapp = angular.module('app', []);
myapp.controller('Ctrl', function ($scope) {
$scope.otherBoxUpdate = function (obj) {
console.log(obj);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="Ctrl as vm">
<input type="text" class="other-box" ng-model="test" id="4" ng-change="otherBoxUpdate(4)"/>
</div>
If you only need to identify the input that was clicked, you can pass some other piece of identifying information to your handler. For example, we can pass the id:
<input type="text" class="other-box" ng-model="test" id="4" ng-change="otherBoxUpdate(4)"/>

Why is my AngularJs 'add' button only working once?

I'm just starting out with Angular.
I've written some code that downloads a JSON array configuredAPIs and displays each object within it, <div ng-repeat="capi in configuredAPIs">. For each of these, there's another directive to list the items from an array of strings, <tr ng-repeat="eurl in capi.externalURLs">
Underneath there's a text box to add a new string to this array, which I've bound to a $scope variable called url.
When I click the 'add' button, everything works - the new string is added to the array, a new row appears in the table.. ..but it only works once. Subsequent clicks on the 'add' button add empty strings to the array (and thus empty text boxes).
What have I done wrong?
index.html
<div ng-app="testApp" ng-controller="testCtrl">
<div ng-repeat="capi in configuredAPIs">
<h1>Configured API</h1>
<p>
Name:
{{ capi.name }}
</p>
<h2>External URLs</h2>
<form ng-submit="addExternalURL(capi)">
<table>
<!-- A row in the table for each string in the array -->
<tr ng-repeat="eurl in capi.externalURLs">
<td>
<input type="text" ng-model="eurl" />
</td>
</tr>
<!-- Final table row to add a new string to the array -->
<tr>
<td>
<input type="text" ng-model="url" placeholder="Enter a new external URL">
<input class="btn-primary" type="submit" value="add">
</td>
</tr>
</table>
</form>
</div>
</div>
controller.js
var app = angular.module('testApp', []);
app.controller('testCtrl', function ($scope, $http) {
$scope.url = 'new url';
$http.get("/api/configuredapis?orgid=2")
.success(function (response) { $scope.configuredAPIs = response; });
$scope.addExternalURL = function ($capi) {
$capi.externalURLs.push($scope.url);
$scope.url = '';
};
});
It is because AngularJS does not watch and update primitives (e.g. strings, numbers, booleans) the way one obviously thinks it does.
So instead you bind objects with values to the scope or use a function which returns the primitive value.
See:
https://github.com/angular/angular.js/wiki/Understanding-Scopes
http://www.codelord.net/2014/05/10/understanding-angulars-magic-dont-bind-to-primitives/
Example for using an object (at controller):
$scope.primitives = {
url : 'foo://'
}
And within the template:
<input type="text" ng-model="primitives.url">
So what happens in your example is that once you set it to '' the changes to the model within the template are not recognized anymore.

Angular js get div element issue

I am trying to get a div element value by ng-click but it comes an alert undefined.
my html is
<div ng-model='myName'>this is my name</div>
<br/>
<input type="button" value="Show Name" ng-click="showName()"/>
script is
$scope.showName = function(){
var nameOne = $scope.myName;
alert(nameOne);
}
How to solve this issue. Thank you.
ngModel directive can't be used with plain div (unless the element is a custom form control), see https://docs.angularjs.org/api/ng/directive/ngModel
<div ng-init="myName = 'this is my name'">{{ myName }}</div>
<br/>
<input type="button" value="Show Name" ng-click="showName()"/>
Plunker here
Edit: using ngInit outside of ngRepeat is considered a bad practice, initialize scope vars in the controller
$scope.myName = 'this is my name';
$scope.showName = function() {
alert($scope.myName);
}
<div>{{myName}}</div>
<br/>
<input type="button" value="Show Name" ng-click="showName()"/>
script is
$scope.showName = function(){
var nameOne = $scope.myName;
alert(nameOne);
}
Actually ng-model should be use on inputs.
<input type="text" ng-model="myName">
This Will create a $scope.myName var and bind it to the input content.
If you intitialize the myName in your javascript and want it to be displayed in this div you should use this syntax :
JS
$scope.myName = "Ben";
HTML
<div>This is my name : {{myName}}</div>
In both case your button click will not show undefined unless you didn't initialize it before the click (on the 2nd exemple).
Hope it helped.

Angular - unable to get selected ng-repeated radio button using ng-submit

I might be missing something really simple here - but if I use ng-repeat to create a bunch of radio buttons - I cannot get the selected one using ng-submit.
The controller simply attaches an array of options to the scope.
The markup simply creates a bunch of radio buttons with ng-repeat within a form. It uses ng-submit to capture the submit event. Click 'Run code snippet' below to see the issue.
angular.module('myApp', [])
.controller('myController', ['$scope', function($scope) {
$scope.selectedoption = "";
$scope.submitCalled = "";
$scope.options=[];
$scope.options[0]={id: "option1", name: "option 1"}
$scope.options[1]={id: "option2", name: "option 2"}
$scope.options[2]={id: "option3", name: "option 3"}
$scope.options[3]={id: "option4", name: "option 4"}
$scope.submitForm = function() {
console.log($scope.selectedoption);
$scope.submitCalled = "submit called " + $scope.selectedoption;
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<form ng-submit="submitForm()" ng-controller="myController">
<div ng-repeat="option in options">
<input type="radio" name="option" ng-value="option.id" ng-model="selectedoption">
<label for="radio">{{option.name}}</label>
</div>
<h2>{{selectedoption}}</h2>
<h2>{{submitCalled}}</h2>
<input type="submit" id="submit" value="Submit" />
</form>
</div>
You ng-repeat div should be using ng-model="$parent.selectedoption"
Reason
ng-repeat creates a new child scope every time, as you are declaring new ng-model inside ng-repeat will be added into the ng-repeat scope(child), this child scope has been created on each iteration by ng-repeat. As you want to create a scope variable to the parent scope, so you need to use $parent variable that will point the parent scope.
<div ng-repeat="option in options">
<input type="radio" name="option" ng-value="option.id" ng-model="$parent.selectedoption">
<label for="radio">{{option.name}}</label>
</div>
Demo Fiddle
Update
Other good way to avoid this sort of $parent annotation is by using object annotation, that will do follow the scope prototypal inheritance. Only thing you need to do is define one scope variable in your controller like $scope.selected = { option: '' } then while using it on html you could refer it as selected.option directly no need to use $parent for parent scope reference.
Demo Fiddle

How to dynamically create multiple form input fields with incremented ng-models?

After reading this article, I understand how to dynamically add a form field using ng-repeat.
I am wondering how can multiple form elements be dynamically created with incrementing ng-model values.
For example, the following would be created from a button click.
<input ng-model="vm.foo.bar1.first">
<input ng-model="vm.foo.bar1.second">
<input ng-model="vm.foo.bar1.third">
<input ng-model="vm.foo.bar1.fourth">
<input ng-model="vm.foo.bar2.first">
<input ng-model="vm.foo.bar2.second">
<input ng-model="vm.foo.bar2.third">
<input ng-model="vm.foo.bar2.fourth">
<input ng-model="vm.foo.bar3.first">
<input ng-model="vm.foo.bar3.second">
<input ng-model="vm.foo.bar3.third">
<input ng-model="vm.foo.bar3.fourth">
How can this be done?
I would suggest to restructure your ViewModel to make vm.foo.bar an array. Then this would be trivial:
<div ng-repeat="item in barItems">
<input ng-model="vm.foo.bar[$index].first">
<input ng-model="vm.foo.bar[$index].second">
<input ng-model="vm.foo.bar[$index].third">
<input ng-model="vm.foo.bar[$index].fourth">
</div>
Or, if you insist, then also
<div ng-repeat="item in barItems" ng-init="outerIdx = $index">
<input ng-repeat='p in ["first", "second", "third", "fourth"]'
ng-model="vm.foo.bar[outerIdx][p]">
</div>
(I'm assuming here, that unlike with first, second, etc..., the number of bars is not known - hence an array is a better option)
EDIT:
If you really want, you could also make vm.foo an object that holds properties bar1, bar2, etc...:
<div ng-repeat="item in [1, 2, 3, 4]">
<input ng-model="vm.foo['bar' + item].first">
<input ng-model="vm.foo['bar' + item].second">
<input ng-model="vm.foo['bar' + item].third">
<input ng-model="vm.foo['bar' + item].fourth">
</div>
but don't forget to first create vm.foo object in the controller:
this.foo = {};
When I have to do this I use the $index to control the names of things. Although I've never tried this exact code, this should work.
<input ng-model='vm.foo.bar3[$index]'></input>
$index comes along whenever you do ng-repeat and is just the index of the list item. So that should end up making ng-models that are vm.foo.bar3.0 to whatever.
To my point of view, you should create arrays of models in your controller.
$scope.vm.foo = [{
bar1: [{
first: '',
second: '',
...
},
bar2: ...
],
}]
And then in your view iterate on your tab :
<div ng-repeat="elem in foo">
<div ng-repeat="input in elem">
<input ng-model="input">
</div>
</div>
Hope it will help you !

Categories

Resources