Knockout.js conditional binding - javascript

How do you have conditional binding based on other properties?
Example..
var ViewModel = {
IsAdded = ko.observable(),
AddedBy = ko.observable()
}
When I display it.. I don't want to show AddedBy if IsAddedBy is null or false
Something like this..
&ltinput type="text" data-bind="value: if (IsAdded != null && IsAdded) { AddedBy }"/>
I know that isn't right, but something like that...

What I would do is this;
var ViewModel = function() {
this.IsAdded = ko.observable('True');
this.AddedBy = ko.observable('Test');
this.AddedByText = ko.computed(function(){
if ( this.AddedBy() != null && this.IsAdded() ) return this.AddedBy()
return "";
}, this);
}
Then your input would be
<input type="text" data-bind="value: AddedByText" />
This way you are keeping the logic contained within your ViewModel and separate from the HTML.

This question is old but it might help someone else looking
<input type="text" data-bind="value: IsAdded ? AddedBy : "" "/>
Basically if IsAdded is not null then set the value to AddedBy, else do nothing

Related

Customize required = true in knockout validation

HTML:
<input class="form-control" type="text" data-bind="value: Participant().FirstName />
Model:
self.FirstName = ko.observable(data === null ? "" : data.FirstName);
ViewModel:
ko.validation.init();
self.ApplyValidations = function () {
var participant = new Participant(null);
participant.FirstName.extend({
required: true
}),
The validation is not working, maybe because I already have 0 or "" passed in text fields.
Can anyone help me with customizing the required so that it avoids "" or 0 and validates the textbox?
Try this in your Model instead.
self.FirstName = ko.observable(self.FirstName || "")

After using $setValidity at the form level, validity is stuck on false

I'm writing a fairly complicated search form in Angular.
The form is broken into sections - you can search by ID, url, text string or location. When you search by text string and location at the same time, I need to validate that you submit latitude and longitude together.
In other words, searching by a text string is fine. Searching by lat / lng is fine. Searching by a text string and latitude but omitting longitude is not ok.
Whenever possible, I'm using HTML5 and angular directives for validating individual fields' contents, but I'm trying to validate a particular combination of values by using a scope watcher, looking at the form object, and using $setValidity() if I discover that the current search mode is incompatible with a particular combination of fields.
My current issue is that, once I've used $setValidity() once, that validation state is "stuck". When the user switches out of 'textOrLocation' search mode, I want to let angular go back to its default validation behavior. I don't understand why it's not doing that - I only call $setValidity() on scope change after checking the form's in 'textOrLocation' mode.
Javascript:
$scope.search = {
mode: 'id'
};
$scope.$watch(textOrLocationValid, function() {});
function textOrLocationValid() {
var usingTextOrLocation = $scope.search.mode == 'textOrLocation';
if (usingTextOrLocation) {
var textModel = $scope.form.searchText || {},
textValid = textModel.$valid,
textValue = textModel.$modelValue,
latModel = $scope.form.searchLat || {},
latValid = latModel.$valid,
latValue = latModel.$modelValue,
lngModel = $scope.form.searchLng || {},
lngValid = lngModel.$valid,
lngValue = lngModel.$modelValue,
formValid = (textValid && latValid && lngValid) && // No invalid fields
((latValue && 1 || 0) + (lngValue && 1 || 0) != 1) && // Either both lat and long have values, or neither do
(textValue || latValue); // Either text or location are filled out
if (formValid) {
// Explicitly set form validity to true
$scope.form.$setValidity('textOrLocation', true);
} else {
// Explicitly set form validity to false
$scope.form.$setValidity('textOrLocation', false);
}
}
}
HTML
<form name="form">
<div ng-if="search.mode == 'id'">
<input type="text" name="searchId" required>
</div>
<div ng-if="search.mode == 'textOrLocation'">
<input type="text" name="searchText">
<input type="number" name="searchLat" min="-90" max="90" step="0.000001">
<input type="number" name="searchLng" min="-180" max="180" step="0.000001">
</div>
<button ng-disabled="form.$invalid">Submit</button>
</form>
My understanding is that because the function is being watched, it's actually being evaluated by Angular periodically during each digest. A simple solution might be to set the validity of textOrLocation to true when that particular form is not in focus. This would allow the button state to depend on the validity of the field in the id form.
function textOrLocationValid() {
var usingTextOrLocation = $scope.search.mode == 'textOrLocation';
if (usingTextOrLocation) {
var textModel = $scope.form.searchText || {},
textValid = textModel.$valid,
textValue = textModel.$modelValue,
latModel = $scope.form.searchLat || {},
latValid = latModel.$valid,
latValue = latModel.$modelValue,
lngModel = $scope.form.searchLng || {},
lngValid = lngModel.$valid,
lngValue = lngModel.$modelValue,
formValid = (textValid && latValid && lngValid) && // No invalid fields
((latValue && 1 || 0) + (lngValue && 1 || 0) != 1) && // Either both lat and long have values, or neither do
(textValue || latValue); // Either text or location are filled out
if (formValid) {
// Explicitly set form validity to true
$scope.form.$setValidity('textOrLocation', true);
} else {
// Explicitly set form validity to false
$scope.form.$setValidity('textOrLocation', false);
}
}
else{
// Explicitly set form validity to true because form is not active
$scope.form.$setValidity('textOrLocation', true);
}
}
To create complex tests, you can use a small directive use-form-error, which may also be useful to you in the future.
With this directive, you can write:
<div ng-form="myForm">
<div>
<input type="text" ng-model="searchText" name="searchText">
<input type="number" ng-model="searchLat" name="searchLat" min="-90" max="90" step="0.000001">
<input type="number" ng-model="searchLng" name="searchLng" min="-180" max="180" step="0.000001">
<span use-form-error="textOrLocation" use-error-expression="textOrLocationValid()" use-error-input="myForm"></span>
</div>
{{myForm.$error}}
<br>
<button ng-disabled="myForm.$invalid">Submit</button>
</div>
And JS
angular.module('ExampleApp', ['use']).controller('ExampleController', function($scope) {
$scope.search = {
mode: 'textOrLocation'
};
$scope.textOrLocationValid = function() {
var usingTextOrLocation = $scope.search.mode == 'textOrLocation';
if (usingTextOrLocation) {
var textModel = $scope.myForm.searchText || {},
textValid = textModel.$valid,
textValue = textModel.$modelValue,
latModel = $scope.myForm.searchLat || {},
latValid = latModel.$valid,
latValue = latModel.$modelValue,
lngModel = $scope.myForm.searchLng || {},
lngValid = lngModel.$valid,
lngValue = lngModel.$modelValue,
formValid = (textValid && latValid && lngValid) && // No invalid fields
((latValue && 1 || 0) + (lngValue && 1 || 0) != 1) && // Either both lat and long have values, or neither do
(textValue || latValue); // Either text or location are filled out
return !formValid;
} else {
// Explicitly set form validity to true because form is not active
return false;
}
}
});
Live example on jsfiddle:
I got it working, you can find code in this Plunker
There were several problems (I'm making assumption, you are trying handling your form in controller, not in custom directive):
$scope.form that we are trying to access in controller is not same form that we have in the view. Form gets its own scope. which is not directly accessible in controller. To fix this, we can attach form to $scope.forms - object, that is declared in controller (read more on inheritance patterns here)
we should attach ng-model to inputs, so we can $watch them, since it not possible to watch form directly (read more here)
in addition, we have to watch $scope.search changes.
It's definitely not the most elegant solution to handle custom validation... Will try to come up with custom directive for that.

AngularJs - facing an issue when using ng-init

I am facing an issue with using ng-init and assign it model inside my html.
The following code works fine. The following code is for Add/Edit functionality. For example, when row is is opened in Edit mode than it persist existing value and shows it in textbox.
<div>
<div ng-if="title == 'Add Student'">
<input type="text" name="name"placeholder="Student Name" data-ng-model="registration.Student.FirstName" maxlength="50">
</div>
<div ng-if="title == 'Edit Student'">
<input type="text" name="name"placeholder="Student Name" data-ng-model="student.Student.FirstName" maxlength="50">
</div>
</div>
However, the following code which is short version of above code does not work. I mean when the row is opened in edit mode it shows text field but does not show existing value (first name) in it. Why?
<div ng-init="model = (title == 'Add Student' ? registration.Student : student.Student)">
<input type="text" name="name" placeholder="Student Name" data-ng-model="model.FirstName" maxlength="50">
</div>
Please suggest whether ng-init can't be used in this way or some issue in my code?
thanks
Controller
var currentState = $state.current.name;
if if (currentState == "Add")
{
$scope.registration = {
Student: {
FirstName: '',
}
var _init = function () {
}
$scope.title = " Add Student";
}
else
{
$scope.student= {};
$scope.student= response[0];
var _init = function () {
$scope.title = " Edit Student";
}
}
You are ng-init block is wrong currently it is returning true or false, you are messing with brackets.
Markup
<div ng-init="model = (title == 'Add Student') ? registration.Student : student.Student">
<input type="text" name="name" placeholder="Student Name" data-ng-model="model.FirstName" maxlength="50">
</div>
Update
In your current case you ng-init is getting executed while element rendered on the DOM, at that instance on time registration.Student & student.Student doesn't have any value. Evaluation of ng-init setting null object to the model student. I'd suggest you do set model value from the controller logic that would be more safer.
Code
var currentState = $state.current.name;
if (currentState == "Add")
{
$scope.registration = {
Student: {
FirstName: '',
}
var _init = function () {
}
$scope.title = " Add Student";
}
else
{
$scope.student= {};
$scope.student= response[0];
var _init = function () {
$scope.title = " Edit Student";
}
//shifted logic in controller
$scope.model = (title == 'Add Student' ? registration.Student : student.Student);
}
Markup
<div>
<input type="text" name="name" placeholder="Student Name"
data-ng-model="model.FirstName" maxlength="50"/>
</div>
Other way you could add one more flag like loadedData which will says that ajax response has been fetched & registration.Student & student.Student values are available in the scope.
Markup
<div ng-if="loadedData">
<input type="text" name="name" placeholder="Student Name" data-ng-model="model.FirstName" maxlength="50">
</div>
Code
var currentState = $state.current.name;
if (currentState == "Add")
{
$scope.registration = {
Student: {
FirstName: '',
}
var _init = function () {
}
$scope.title = " Add Student";
}
else
{
$scope.student= {};
$scope.student= response[0];
var _init = function () {
$scope.title = " Edit Student";
}
//set flag
$scope.loadedData = true;
}

ng-model in input based on another variable

I han an input filed in my angular app <input type="text" ng-model="variableA">. I want to use variableA or variableB basing on some condition. I've tried something like this ng-model="condition ? variableA : variableB" but it throws Error: [ngModel:nonassign]. Any ideas how to make it work?
You could make use of ngModelOptions:
<input type="text" ng-model="setModel" ng-model-options="{getterSetter: true}">
In your controller:
$scope.setModel = function(value) {
var model = '';
if (condition) {
model = "variableA";
} else {
model = "variableB";
}
if (angular.isDefined(value)) {
return ($scope[model] = value);
} else {
return $scope[model];
}
};
ngModel has to be a property expression (following the fules of $parse).
Try using a proxy then watch that variable to then assign to A or B:
<input type="text" ng-model="variableC">
In your controller:
$scope.$watch('variableC', function(value) {
if(someCondition) {
$scope.variableA = value;
} else {
$scope.variableB = value;
}
});
How about
<input type="text" ng-model="variableC">
and in controller
$scope.variableC = (condition) ? variableA : variableB

Knockout.js: Set a default text if a binding has a given value

I'd like to reset the value of an input field (text='') whenever model.id is null.
How to bind the input value to respond to a certain value of an observable object? Something that would look like:
<input type="text" data-bind="text: if (model.value == null) { '' }" />
You can use ? operator in data-bind attribute:
<input type="text" data-bind="value: model.id() == null ? 'Default Value' : model.value()" />
In your viewmodel, initiate value of the property as follow :
var model.value = ko.observable('');
In HTML, you don't have to use confitional expression
data-bind="text: model.value"
check these codes
<input type="text" data-bind="value: id() == true? 'Value is Red' : value()" />
function viewModel() {
this.id = ko.observable(true);
this.value = ko.observable("Value is Green");
}
ko.applyBindings(new viewModel());
http://jsfiddle.net/d4SKr/
The correct answer should be to create a computed observable to get the label.
self.getLabel = ko.pureComputed(function() {
return this.value() === null ? 'Value is red' : value();
});
<input type="text" data-bind="text: getLabel" />

Categories

Resources