Angular: ng-model defined only when select is dirty - javascript

The issue I'm having is that I'd like to have a text input be blank while it has a defined ng-model that has a default value, then, when the input becomes dirty, I'd like that specific ng-model to be redefined. Example:
<span ng-init="level='Base'">{{level}}</span>
<input ng-model="level" placeholder="Specify Level">
Optimally, the example above would say "Base" as a default value in the span, but not in the input (so the placeholder is visible) until the input becomes dirty with the redefined 'level' value. I've tried something along these lines, but was unable to achieve what I was looking for:
<span ng-init="level='Base'">{{level}}</span>
<input ng-model="noop" ng-change="checkDirty()" name="lvl" id="lvl" placeholder="Specify Level">
// Controller code
$scope.checkDirty = function(){
if ($scope.myform.lvl.$dirty) {
angular.element("#lvl").attr("ng-model", "level")
}
}
Thanks for your time!

You don't need to have a change event to check if the form is dirty. You can directly have a condition inside the span element.
{{level ? level : 'Base'}}
Demo
var app = angular.module('app', []);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<span>{{level ? level : 'Base'}}</span>
<input ng-model="level" name="lvl" id="lvl" placeholder="Enter level">
</body>

Related

Why ng-Model is not updated in HTML input element when I set element.value in JavaScript?

For example I have HTML input field:
<input id="firstName" ng-model="applicant.firstName" type="text">
<button ng-click="updateFirstName()">Update</button>
<h4>See content of ngModel: {{applicant.firstName}}</h4>
then in AngularJS Controller I have method that uses pure JavaScript to get element by Id and update its value:
$scope.updateFirstName = function() {
var element = document.getElementById("firstName");
element.value = 'Updated!';
};
So when I click Update button, input element value gets successfully updated, but ng-model is not. It suppose to create $scope.applicant.firstName and assign value to it, but it does not happen.
It gets updated only when I type something in HTML input text box.
How can I make ng-model to get updated when I change is from JS code through element? May there is a way to trigger something when HTML input value is changed?
I know if I use pure AngularJS, I can just inside Controller call:
$scope.applicant = {};
$scope.applicant.firstName = "Updated!";
and it will update both with no problems, but in the specifics of this task, I have to use JS element and update its value.
If $scope.applicant.firstName doesn't already exist, ng-model will not create it for you.
You use ng-model to bind an existing variable to an HTML element.
Note the example from than Angular Docs declares $scope.val in the controller.
<script>
angular.module('inputExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.val = '1';
}]);
</script>
<style>
.my-input {
transition:all linear 0.5s;
background: transparent;
}
.my-input.ng-invalid {
color:white;
background: red;
}
</style>
<p id="inputDescription">
Update input to see transitions when valid/invalid.
Integer is a valid value.
</p>
<form name="testForm" ng-controller="ExampleController">
<input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
aria-describedby="inputDescription" />
</form>

Angular input inside of directive

I have an angular directive that I'm building with some pagination controls.
<div>
<!-- table logic here -->
</div>
<div>
<button bg-click="current=prevPage(current);">Prev</button>
<input ng-model="current" />
/{{pages}}
<button bg-click="current=nextPage(current);">Next</button>
</div>
It works fine, but of course when you change the value in the input.
It will also pick up the empty string during the delete before you enter a new value in.
Is there a way to sniff that out in the directive before it actually fires the change in the current value.
You can just check the value of the variable current before do pagination.
I assumes that you are using $watch() to listen the change of your variable current.
code inside your directive's link,
scope.$watch("current", function(newVal, oldVal){
if(angular.isNumber(newVal)){
// your variable is a number
// Do your pagination here
}
});
ngModelOptions is useful in such a case. You could write like this
<input ng-model="current" ng-model-options="{ updateOn: 'default', debounce: {'default': 500, 'blur': 0} }" />
to delay update value to model 500ms in default and update immediately if lost focus.
That question also has been answered several times, so you should be able to find further information in the other questions.
UPDATE:
$scope.$watch(
function() {
return $scope.current;
}, function(oldVal, newVal) {
if (!newVal) {
$scope.current = oldVal
}
}
);
If oldVal is also null or an empty string, you could copy $scope.current to another scope variable (say $scope.savedCurrent) whenever newVal has a value and copy it back to $scope.current if not.
I maybe understand wrong your question.
This will update the model only when you click outside the input
<input ng-model="current" ng-model-options="{updateOn: 'blur'}" />
Or another way, when you change the input value it call a function who test if the new value is valid and set it in current.
Html :
<div>
<button bg-click="current=prevPage(current);">Prev</button>
<input type="number" ng-model="currentInput" ng-change="changeCurrent()" />
/{{pages}}
<button bg-click="current=nextPage(current);">Next</button>
</div>
Controller :
$scope.currentInput = $scope.current;
$scope.changeCurrent = function(){
if ($scope.currentInput!=""){
$scope.current = $scope.currentInput;
}
}

Binding ngModel to expression's value

I have a scenario in which the property ng-model should bind to value that comes from database as a part of business logic.
For simplicity, I have created this example
function TodoCtrl($scope) {
$scope.field1 = "PropertyFromDB";
$scope.PropertyFromDB = "hello world";
}
<div ng-app>
<h2>ngModel BINDING EXAMPLE</h2><br/>
<div ng-controller="TodoCtrl">
<div ng-init="imod = field1">
<input type="text" ng-model="imod"></input>
</div>
</div>
</div>
In this example, field1 is the value which will be property that comes from DB (i.e. PropertyFromDB) and ng-model should bind to PropertyFromDB instead of field1.
So, its like I want to evaluate expression inside expression syntax of ng-model but I am unable to do so.
Thanks
You can refer to the current scope with this and read its property, like:
<div ng-init="imod = field1">
<input type="text" ng-model="this[imod]"></input>
</div>
but it is a very unusual and inconvenient way to create a View Model.
I suggest creating a better structure/object that contains the field/value combination and setting that object on the scope, instead of setting field and value separately the scope.
You simply cannot set value of ng-model by using any expression. The alternative would be to change the stucture of the JSON which you returned from server.
Markup
<div ng-controller="TodoCtrl">
<div ng-repeat="field in properties">
<label>{{field.property}}</label>
<input type="text" ng-model="field.value" />
</div>{{properties}}
</div>
Code
function TodoCtrl($scope) {
$scope.properties = [{
property: 'PropertyFromDB',
value: "hello world"
}, {
property: 'PropertyFromDB1',
value: "hello world1"
}]
}
Fiddle Here

Angularjs - assigning logic between two models

I am new to Angular. It is a very simple question -
in my index.html I am defining two models on two text boxes :-
<html><head><script...></head><body ng-app="myApp"ng-controller="MainController" >
<input ng-model="tb1" type="text" name="numberofusers"/>
<input ng-model="tb2" type="text"></input>
</body></html>
And in my app.js I am defining like this
var app = angular.module('myApp', []);
app.controller('MainController', ['$scope', function($scope){
$scope.tb1 = $scope.tb2;
}]);
Now, what I want is that whatever I type in first text box (tb1) automatically typed to second text box (tb2) and vise-versa, but that is not happening.
Any guess ?
Your code in controller $scope.tb1 = $scope.tb2; would only be executed once (when controller initializes), that's why it doesn't work.
You need to bind input elements to the same model then Angular will handle two-way binding for you automatically.
<input ng-model="tb1" type="text" name="numberofusers"/>
<input ng-model="tb1" type="text"></input>
Or if you want to use two different models for different elements, you can add a hook to input's ng-change event listener like
<input ng-model="tb1" type="text" name="numberofusers" ng-change="tb2 = tb1"/>
<input ng-model="tb2" type="text" ng-change="tb1 = tb2"></input>
Then these two elements would sync automatically. But you know what, ng-change can only monitor user input change, that means, if you change tb1 or tb2 programmably, ng-change will not be triggered.
In this case, you should monitor model's change using $scope.$watch
$scope.$watch('tb1', function(newValue) {
$scope.tb2 = newValue;
}));
Currently it's beyond your requirement.
This is because controller will only execute once and if there is any value in $scope.tb2 will assign to $scope.tb1 but intially both of them are blank .
So you need to $watch the changes and assign value to each other like :-
$scope.$watch('tb1',function(newVal){
$scope.tb2=newVal;
})
$scope.$watch('tb2',function(newVal){
$scope.tb1=newVal;
})
And if you want to manage it on front end you can use ng-change directive like
<input ng-model="tb1" type="text" ng-change="tb2=tb1" name="numberofusers"/>
<input ng-model="tb2" type="text" ng-change="tb1=tb2"></input>
You can use two-way binding to achieve that. An example is: JSFiddle
Create your directive:
var app = angular.module('myApp', []);
app.controller("myCtrl", function($scope) {
$scope.myName = 'Carl';
}).directive("myDirective", function() {
return {
restrict: 'AE',
scope: {
twowayBindingProp: '=myName'
}
}
});
And bind it through:
<div ng-app="myApp">
<div ng-controller="myCtrl">
<h1>From parent: </h1>
<h3>parentProp2: <input ng-model="myName"></h3>
<div my-directive my-name="myName">
<h1>From child: </h1>
<h3>twowayBindingProp: {{ twowayBindingProp }}</h3>
<h1>Set from child:</h1>
twowayBindingProp: <input ng-model="twowayBindingProp">
</div>
</div>
</div>

Hide and Show elements based on values in Array in AngularJS

I have the following HTML code:
<div ng-controller="DemoController">
<label class="list-group-item" ng-repeat="option in DesignOptions">
<input type="radio" name="optionsRadios" value="{{option[0]}}" />
{{option[1]}}</label>
<label class="list-group-item" ng-repeat="option in StyleOptions">
<input type="checkbox" value="{{option[1]}}">
{{option[2]}}
</label>
</div
And I have the following AngularJS code:
<script type="text/javascript">
var app = angular.module('myApp', []);
app.controller('DemoController', function ($scope) {
var json = '{"Table":[[4,"Full"],[5,"Half"]],"Table1":[[4,1,"Elbow Patch"],[5,2,"Roll Up"]]}';
var obj = $.parseJSON(json);
$scope.DesignOptions = obj.Table;
$scope.StyleOptions = obj.Table1;
});
</script>
This gives me the following result:
Now, I need to display Elbow Patch checkbox only when Full radio button is selected. And Roll Up when Half radio button is selected. This is because, if you see obj.Table array, it has id of '4' for Full and obj.Table1 has id of '4' for Elbow Patch and so on.
I tried Angularjs - showing element based on a presence of id in array but could not modify it to work in my case as my array is very different.
Add a new property to your controller which will store the selected design option:
$scope.designOption = 4; // default to Full
Add the binding to this property in the view:
<input ng-model="$parent.designOption" type="radio" value="{{option[0]}}" />
Add an ng-show directive to the checkbox label:
<label ng-repeat="option in StyleOptions" ng-show='option[0] == designOption'>
I've removed the class and name attributes from the element just to make the code clearer.
NB Need to reference the $parent scope on the radio input as the ng-repeat directive will create a scope for each repeated element and javascript prototypical inheritance rules means that using just 'designOption' will create a designOption property on the child scope and not use the one in your controller.

Categories