How to pass anything from DOM-element into function defined in $scope - javascript

Example:
<div ng-repeat="obj in objList">
<input type="text" id="obj_{{obj.id}}"/>
<button ng-click="removeRow(NEED PARAM HERE)">Remove</button>
</div>
I could use button's id or maybe parent's, but I dont know exactly how.
And second similar case: when I want get some value from input, for example. How can I do it?

Just pass obj in your function, and then remove the object obj from objList in your controller, it will disappear from your view, that's how angular's data binding works :
<div ng-repeat="obj in objList">
<input type="text" id="obj_{{obj.id}}"/>
<button ng-click="removeRow(obj)">Remove</button>
</div>
And in you controller:
$scope.removeRow = function(obj) {
var index = $scope.objList.indexOf(obj);
$scope.objList.splice(index, 1);
}

A little hard to follow your question, but are you trying to pass the value from the text field to the function so you can remove the row from the list?
If that is true then the you want to do something like this.
You'll want to use the ngRepeat track by functionality. See https://docs.angularjs.org/api/ng/directive/ngRepeat
HTML
<div ng-repeat="obj in objList track by $index">
<input type="text" id="obj_{{obj.id}}" />
<button ng-click="removeRow($index)">Remove</button>
</div>
At that point you're just using basic Javascript splice functionality to remove an item from an Array by index. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
JS
$scope.removeRow = function($index) {
$scope.objList.splice($index, 1);
};
See also AngularJS How to remove an Item from scope

Related

How to add and remove text field on click of a button in angular js

I have generated button using ng-repeat directive in angular js. Now I want to generate a textfield inside a div tag on click of that button.
Example buttons -
Example textfields added on click of it -
I am doing this using innerHTML attribute of div tag, like below -
var txt = document.getElementById("paidby").innerHTML;
document.getElementById("paidby").innerHTML=txt+ "<div class='row' style='padding:2px'><div class='col-sm-4'><input type='number' placeholder="+str+"></div></div>";
But I want to know if there is any other better way using angularJS or javascript to do the same so that if I need to remove one or all of the textfields later on, it can be done easily. By removing means deleting and NOT hiding.
(becuase if I want to remove for example textfield 'two' now, I have no idea how I remove it)
You don't want to manipulate the DOM within your controller. Often times, there are better ways to do this within the framework that Angular provides.
You can do this by having another ng-repeat which loops over an array you declare within your controller. For example:
In your view:
<section id="paidby" ng-repeat="textfield in textfields">
<div class='row' style='padding:2px'>
<div class='col-sm-4'>
<input type='number' placeholder="{{textField.str}}" ng-model="textField.value">
</div>
</div>
</section>
In your controller, within your button ng-click logic:
// To add:
$scope.textFields.push({ str: someVariable, value: someValue });
// To remove:
var index = $scope.textFields.map(function(t) { return t.value; }).indexOf(someValue);
if (index !== -1) {
$scope.textFields.splice(index, 1);
}
Try hiding the inputs to start with, then show them if the appropriate button is clicked:
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<div ng-app="" ng-init="array=['one','two','three','four','five']; show=[false,false,false,false,false]">
<button ng-repeat="item in array" ng-click="show[$index] = !show[$index]">{{item}}</button>
<br>
<input type="text" ng-repeat="item in array" placeholder="{{item}}" ng-if="show[$index]" />
</div>

How to validate emails onEqual in several divs with them on the same page

The situation:
<body>
<div id="1">
<input type="text" name="email_1"/>
<input type="text" name="email_2"/>
</div>
<div id="2">
<input type="text" name="email_3"/>
<input type="text" name="email_4"/>
</div>
<!--and so on...-->
</body>
And I need to validate inputs inside these 2 inputs inside every div to be equal(only inside div). Maybe the main problem is that all divs are dynamically generated, and we don't know exactly their quantity to provide knockout support. How to do that? What is the most elegant solution?
Update 1
I've tried:
1. To make some binding using knockout model. But my solution for this
was to create some observable property to check inputs values. This
is bad way I guess.
2. To use jquery for this. Tried to validate fields via validate class for
inputs(http://jqueryvalidation.org/jQuery.validator.addClassRules/)
Update 2
My solution was something like that:
<div id="1">
<input type="text" name="email_1"/>
<input type="text" name="email_2"/>
<label data-bind="visible: checkEmailsEquality(email_1,email_2)">Emails must be equal</label>
</div>
But this solution is not ok, because this binding works only once - at page loading, what isn't good. I need to bind this check to text update in these inputs, and I don't know how.
Update 3
My suggestion is to deal with it in this way:
Make on the first email input this binding http://knockoutjs.com/documentation/textinput-binding.html
Bind with similar function in knockout's model as wrote Wayne Ellery.
If values aren't equal make error label visible.
The main condition is to pass apropriate inputs ids to function, and I guess this will work.
The simplest way to do this is to just use a computed which will compare the two emails.
Below in ViewModel I'm using the jQuery $.map method to map all items to an array of Item objects.
var ViewModel = function (model) {
var self = this;
self.items = $.map(model.items, function(item) { return new Item(item) });
};
Here I'm using a computed method in Item to compare email1 and email2.
var Item = function (item) {
var self = this;
self.email1 = ko.observable(item.email1);
self.email2 = ko.observable(item.email2);
self.areEmailsSame = ko.computed(function() {
return self.email1() === self.email2();
});
};
http://jsfiddle.net/pxar0587/1/
I've found maybe the most elegant solution, it's about using equalTo attribute, e.g.:
<div id="1">
<input type="text" id="1" name="email_1" equalTo="#2"/>
<input type="text" id="2" name="email_2" equalTo="#1"/>
</div>
Hope this help somebody.

Using input field for both default and user-defined filter with AngularJS

There must be a simple answer to this, but I'm not getting it. I'm using the following input element to filter the results in a table through AngularJS. If I manually type "foo" into the input field, the table will filter for "foo". But my hard-coded value of "foo" (in the input's value attribute) will neither show up in the browser nor filter the results upon page load. How can I make this input field provide both a default filter and a user-defined filter? Thanks in advance.
<input ng-model="search" id="search" type="text" placeholder="Search" value="foo">
<div ng-controller="eventsController")>
<table>
<tr ng-repeat="event in events | filter:search">
<td><span ng-bind="event.title"></span></td>
<td><span ng-bind="event.date_start"></span></td>
</tr>
</table>
</div>
<script>
function EventsController($scope, $http) {
$http.get('/api/all-events').success(function(events) {
$scope.events = events;
});
}
</script>
If you are using ng-model then you shouldn't set your value in html. Instead you should be setting you model value in Angular. So get rid of you value="foo" and do this.
function EventsController($scope, $http) {
$http.get('/api/all-events').success(function(events) {
$scope.events = events;
$scope.search = "foo"; //This will set the models search to foo
});
}
Edited, initial response is an option but not recommended.
According to the documentation for ng-init
The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
The link is a working example of the "proper" way to go about it.
http://plnkr.co/edit/EyV1R4WUyu2TEMTue3jT

Check $pristine status of ngModel without using a form

Am trying to figure out how to check the state of a ngModel without using a form tag. I don't have wrappers is just basic input element with a ngModel.
All the examples I have found so far are for form validations and in this case, there is no form.
When i tried something like:
HTML
<input type="text" ng-model="lastname">
SCRIPT:
if($scope.lastname.$dirty) {
console.log('last name has changed');
}
I get undefined.
Is there a way to check the state of the ngModel without adding a watch directive to it? it seems it would be something basic that is part of the framework. Why wouldn't this work?
There are two ways:
1. Use ng-form:
<span ng-form="myForm">
<input type="text" name="name" ng-model="name" required/>
</span>
Now you can access the model either at $scope.myForm.namein your controller or with myForm.name in your view:
var isPristine = $scope.myForm.name.$pristine;
2. Use angular.element().controller('ngModel') (Don't do this one, bad bad bad)
Alternatively, you could hack your way around it. But this is going to be ugly, untestable, and gross:
var elem = angular.element(document.getElementById('myElement'));
var model = elem.controller('ngModel');
var isPristine = model.$pristine;
Edit: Your situation (per your comment) inside of a repeater
the only difference between my example and your is that the input field is inside a ng-repeater. Thought that wouldn't matter but I guess it does.
And now it's time to ask yourself what you're doing and why... You can still get the information you need using ng-form, but you'll need to do some crazy stuff I wouldn't recommend:
<div ng-repeater="item in items track by $index">
<span ng-form="rptrForm">
<input type="text" name="name" ng-model="item.name" required/>
</span>
</div>
.. commence craziness:
// get the first child scope (from the repeater)
var child = $scope.$$childHead;
while(child) {
var isPristine = child.rptrForm.$pristine;
var item = child.item;
if(!isPristine) {
// do something with item
}
child = child.$$nextSibling;
}
It's probably time to rethink your strategy
I'm not sure what your end goal is, but you might want to rethink how you're going about it and why. Why do you need programmatic access to $pristine in your controller? What alternatives are there? Etc.
I, for one, would try to leverage an ng-change event and update some flag on my item in my repeater, and leave the ng-form stuff for validation:
<div ng-repeat="item in items track by $index" ng-form="rptrForm">
<input type="text" name="name" ng-model="item.name" ng-change="item.nameChanged = true" required/>
<span ng-show="rptrForm.name.$error.required>Required</span>
</div>
If you give the <form> element a name attribute, then the <form> will be
added to the $scope object as a property.
Field controller will then be attached to the form property.
As weird as it could seem, you have to define an enclosing form with a name attribute like so:
<form name="myForm">
<input type="text" name="lastName" ng-model="lastname">
</form>
and call the property with:
$scope.myForm.lastname.$dirty
Indeed, ngModelController (field) is attached to ngFormController (form).
I used Ben Lesh's answer to deal with the same problem. I was displaying a list of notification preferences, and my goal was to hit my api with models that had changed. Not just ng-changed, either; value changed.
I start by initializing some private state on my controller:
// Private state
var originalModels = {};
var changedModels = [];
Then I store copies of the original models retrieved from the API:
// Create a hash of copies of our incoming data, keyed by the unique Code
for (var i = 0; i <= data.length - 1; i++) {
var np = data[i];
originalModels[np.Code] = angular.copy(np);
}
Next, we want to make sure that when a model changes, we add it to a changed models collection (or remove it from the collection if no real change occurred):
function modelChanged(m) {
var originalModel = originalModels[m.Code];
var hasChanged = !angular.equals(originalModel, m);
// If the model has changed and is not present in our collection of changed models, add it
if (hasChanged && changedModels.indexOf(m) === -1) {
changedModels.push(m);
}
// If the model has not changed and is present in our collection of changed models, remove it
if (!hasChanged && changedModels.indexOf(m) > -1) {
var i = changedModels.indexOf(m);
changedModels.splice(i, 1);
}
}
Finally, we tie these together in our ng-repeat:
<tr ng-repeat="np in npc.notificationPreferences">
<td>{{np.DisplayName}}</td>
<td>
<input type="checkbox"
ng-model="np.Sms"
ng-checked="np.Sms"
ng-disabled="!np.SmsEnabled"
ng-change="npc.modelChanged(np)"/>
</td>
</tr>
Now, instead of pushing back every row to my API, I simply push the changed models. This also has a side benefit of being able to ng-disable your submit button if nothing has actually changed (ie the changedModels collection is empty).

Setting values on repeater scopes

I have an input within a repeater scope set up so that users can modify the value and see the calculated results on that value on that row - the intent is to have a spreadsheet like behavior.
I'd like to know what the best way to automatically populate the input box is, so that the field comes up set to a certain value with the model properly updated. I've set up a fiddle that tries to jam the number 4 into the value attribute here:
http://jsfiddle.net/BVRzh/
Where this is the template:
<div ng-app>
<ol ng-controller="TestCtrl">
<li ng-repeat="a in arr">
<input type="text" ng-model="testVal" value="4"></input>
<span>Value: {{testVal}}, Times {{a}}: {{testVal * a}}</span>
</li>
</ol>
</div>
And this is the javascript:
function TestCtrl($scope) {
$scope.arr = [1, 2, 3, 4];
}
Is there a better way to approach this?
ngInit can be useful :
<input type="text" ng-model="testVal" ng-init="testVal = a">
Example: http://jsfiddle.net/BVRzh/3/
If i understand correctly , this is the only case when you can use ngInit. Citation from documentation:
The only appropriate use of ngInit for aliasing special properties of
ngRepeat, as seen in the demo bellow. Besides this case, you should
use controllers rather than ngInit to initialize values on a scope.

Categories

Resources