angular controller only picks up the input values changed - javascript

I've got a fairly simple angular controller method :
$scope.addComment = function () {
if ($scope.newComment.message) {
$scope.can_add_comments = false;
new Comments({ comment: $scope.newComment }).$save(function (comment) {
$scope.comments.unshift(comment);
return $scope.can_add_comments = true;
});
return $scope.newComment = {};
}
};
And in my form I have a textarea that holds the value of comment :
<textarea class="required" cols="40" id="new_comment_message" maxlength="2500" ng-disabled="!can_add_comments" ng-model="newComment.message" required="required" rows="20"></textarea>
Works great so far, however I do want to send some data, hidden data with the comment as well. So I added something to hold that value :
<input id="hash_id" name="hash_id" ng-init="__1515604539_122642" ng-model="newComment.hash_id" type="hidden" value="__1515604539_122642">
However when I inspect the $scope.newComment it always comes back as an object with only message as it's property, which is the value from the text area, and I don't get the property on the object hash_id.
When I make this input non hidden and I manually type in the value into the input field and submit a form, I do get a object with hash_id property. What am I doing wrong here, not setting it right?

As far as I know, ng-model doesn't use the value property as a "default" (i.e. it won't copy it back into your model). If you want a default, it should be placed wherever the model is defined:
$scope.newComment = { hash_id: "__1515604539_122642", /* Other params */ };
Alternatively, changing the ng-init to an assignment should work (though I would recommend the above solution instead):
<input id="hash_id" name="hash_id" ng-init="newComment.hash_id = '__1515604539_122642'" ng-model="newComment.hash_id" type="hidden">

Related

Validators.required throws an error, even though there is a value inside of it

my issue is that I have a MatInput as follows:
<input matInput [(ngModel)]="name" i18n-placeholder="##Placeholder" placeholder="Your name" [formControl]="InputControl" value="{{ fieldName }}">
In the correspoding component I declared fieldName: string; and I am setting the value in the ngOnInit() method:
ngOnInit() {
this.fieldName = "VALUE"; // obtained from server so it may differ
if(this.fieldName.startsWith("PREFIX")) { // if value starts with a given prefix I want to remove it
this.fieldName = this.fieldName.substr(3);
}
}
The thing is that it's actually working well (value gets displayed properly), but as soon as I want to submit the MatDialog form it tells me that I can't submit an empty form! Thats because I added a 'required' validator:
InputControl = new FormControl("", [Validators.required]);
So there is a value in the input field, but apparently Angular doesn't notice it? If I add a space to the value and delete it afterwards the value is exactly the same, but then it doesn't throw any errors.
Does anyone know how to fix this? It's really annoying.
value attributes are worthless in Angular.
If you want to give your control a value, do it through the control itself :
<input matInput [(ngModel)]="name" i18n-placeholder="##Placeholder" placeholder="Your name" [formControl]="InputControl">
ngOnInit() {
this.fieldName = "VALUE"; // obtained from server so it may differ
if(this.fieldName.startsWith("PREFIX")) {
this.fieldName = this.fieldName.substr(3);
}
// w/ form control
this.InputControl.setValue(this.fieldName);
// w/ template driven
this.name = this.fieldName;
}

2 way data binding in JavaScript

Two-way data binding refers to the ability to bind changes to an object’s properties to changes in the UI, and vice-versa.
Can we achieve 2-way data-binding with JavaScript?
Especially 2 Way Data Binding without Frameworks.
When an input is changed update the value, add a setter to the value which sets the inputs content. E.g this element:
<input id="age">
And some js:
var person = (function(el){
return {
set age(v){
el.value = v;
},
get age(){
return el.value;
}
};
})(document.getElementById("age"));
So you can do:
person.age = 15;
And the input will change. Changing the input changes person.age
Yes, we can achieve the two way data binding using pure javascript.
twoWay=function(event) {
var elem = document.getElementsByClassName(event.currentTarget.className);
for(var key in elem){
elem[key].value=event.currentTarget.value;
}
}
You can check the jsfiddle.
Simple and working approach to two-way binding, only using vanilla JS.
<!-- index.html -->
<form action="#" onsubmit="vm.onSubmit(event, this)">
<input onchange="vm.username=this.value" type="text" id="Username">
<input type="submit" id="Submit">
</form>
<script src="vm.js"></script>
// vm.js - vanialla JS
let vm = {
_username: "",
get username() {
return this._username;
},
set username(value) {
this._username = value;
},
onSubmit: function (event, element) {
console.log(this.username);
}
}
JS Getters and Setters are quite nice for this - especially when you look at the browser support.
Yes indeed.
There are frameworks like angular Js which provides full support for two way data binding.
And if you want to achieve the same in vanilla js you can bind value into view
Eg. document.getElementById('test').value="This is a Test"
And to bind view value to the controller you can trigger onchange event in html.
<Input type="text" id="test" onchange="Func()">
LemonadeJS is another micro-library (4K), with no dependencies worth looking at.
https://lemonadejs.net
https://github.com/lemonadejs/lemonadejs
Adding a little elaboration to Jonas Wilms answer, here's a sample without currying and also adds event binding for a full two way bind.
// Property binding
var person = {
set name(v) {
document.getElementById('name').value = v;
},
get name() {
return document.getElementById('name').value;
},
set age(v) {
document.getElementById('age').value = v;
},
get age() {
return document.getElementById('age').value;
}
};
// You can now set values as such
person.name = 'Cesar';
person.age = 12;
// Event binding completes the two way
function logToConsole(event) {
console.log(event.target.value);
}
// You can set person.name or person.age in console as well.
<label for="name">Name: </label><input id="name" onkeyup="logToConsole(event)">
<label for="age">Age: </label><input id="age" onkeyup="logToConsole(event)">
Would you mind if it would be a small component for databinding tasks that provides enough convenient databinding definition commands. I did it with databindjs. e.g.
// Lets assume that there is just simple form (target)
var simpleForm = {
input: $('.simple .input-value'),
output: $('.simple .output-value')
};
// And here is the simple model object (source)
var model = {
text: 'initial value'
};
// Lets set two directional binding between [input] <-> [text]
var simpleBinding = bindTo(simpleForm, () => model, {
'input.val': 'text', // bind to user input
'output.text': 'text' // simple region that will react on user input
});
// This command will sync values from source to target (from model to view)
updateLayout(simpleBinding);
subscribeToChange(simpleBinding, () => {
$('.simple .console').html(JSON.stringify(model));
});
// Just initialize console from default model state
$('.simple .console').html(JSON.stringify(model));
The full solution here.
You can check the full implementation of the databinding core on github

ng-repeat not updating information after $scope was updated

I have an ng-repeat which creates a form with some starting data. Then the user is free to modify said data and the changes should appear in the form. Before that, the user submitted data are sanitized by another function, which is called by an ng-click on a button.
Everything works well under the hood (I checked my $scope.some_array, from which ng-repeat takes the data and the new data is in the right place) but nothing happens on the page.
The element:
<li ng-repeat="field in some_array" id="field-{{$index}}">
<div class="{{field.field_color}}">
<button type="button" ng-click="save_field($index)">done</button>
{{field.nice_name}}
</div>
<div id="field-input-{{$index}}">
<input type="text" id="{{field.tag}}" value="{{field.content}}">
<label for="{{field.tag}}">{{field.nice_name}}</label>
</div>
</li>
save_field code:
$scope.save_field = function (index) {
console.log($scope.some_array[index]["content"])
var value = $("#field-" + index).children("div").children("input").val()
var name = $scope.some_array[index]["name"]
var clean_value = my_clean(value)
if (norm_value === "") {
return
}
$scope.some_array[index]["content"] = clean_value
console.log($scope.some_array[index]["content"])
}
On the console I see:
10.03.16
10/03/16
Which is right, but in the form I only see 10.03.16. I already tried putting $timeout(function(){$scope.$apply()}) as the last line of my function, but the output is still the same.
You shouldn't use input like this if you want to bind a variable to it. Digest loop will refresh the value but it will not be updated visibly because this is not html native behavior.
Use ng-model instead, it will update view value of the input as expected:
<input type="text" id="{{field.tag}}" ng-model="field.content">
Also using ng-model your variable will be updated when user modify the input, so you can retrieve it to do some treatments much more easily in save_field function, without jQuery:
$scope.save_field = function (index) {
if (norm_value === "") {
return;
}
$scope.some_array[index]["content"] = my_clean($scope.some_array[index]["content"]);
};
More infos: https://docs.angularjs.org/api/ng/directive/ngModel

Can Knockout.js change variable which is not in the viewModel

This is my sample code
HTML:
<input type="text" data-bind="value: a"/>
And in JavaScript I want something like.
var a = 5;
a = ko.observable(a);
But i want to keep a number. And when i change a the input to change and when I change the input a to change.
ViewModel->Scope is possible, but Scope->ViewModel is not possible:
var nonObservable = 5,
observable = ko.observable(nonObservable);
// ViewModel->Scope
observable.subscribe(function(newValue) {
nonObservable = newValue;
});
// Scope->ViewModel
observable("new value");
// This however, will not work
nonObservable = "not in any way connected";
The reason for this is that there is no way for knockout to detect what name you are binding it to (ko.observable just knows that it has been given the value 5, not that it has been given the name nonObservable) ... and even if it could detect this, you would probably not want to do this by default.

How do I get a handle to a dynamically-generated field name in javascript?

I have a series of fields created dynamically based on database records. They will be named cardObject1, cardObject2, and so on for as many rows as necessary. I'm now trying to access a specific cardObject field in a function where the number is passed in, but am getting an error message.
The field looks like this:
<input name="cardObject241" value="2,$25.00,1" type="hidden">
The js code I'm using looks like this:
function deleteFromCart(id){
if (confirm("Are you sure you want to delete this item from your cart?")){
var voucherNbr = document.getElementById("voucherNbr").value;
var cardObjectArray = document.getElementById("cardObject"+id).value.split();
var amtToDelete = cardObjectArray[1];
alert("need to delete " + amtToDelete);
}
}
And the error I'm getting is
document.getElementById("cardObject" + id) is null
on this line:
var cardObjectArray = document.getElementById("cardObject"+id).value.split();
How can I get a handle to the cardObject field that ends with the number passed in as the id param?
You need to add an id="" attribute with the same name as the name attribute.
<input id="cardObject241" name="cardObject241" value="2,$25.00,1" type="hidden">
Firstly, your input field needs an id as well as a name, so it would look like this:
<input name="cardObject241" id="cardObject241" value="2,$25.00,1" type="hidden">
Secondly, if you have an object that may or may not exist, it's always a good idea to check for existence before you start manipulating properties:
var tempObj=document.getElementById("cardObject"+id)
if(tempObj) {
var cardObjectArray = tempObj.value.split();
...do your stuff with cardObjectArray....
}
You can use document.getElementsByName() or (cross-browser back to the Stone Age)
document.forms[formIndexOrName].elements["cardObject" + id].value.split(",")

Categories

Resources