24

In knockout.js I have a very standard field that looks like this:

<label data-bind="text: JobTitle"></label>

What I'd like is to have a default value specified in here if the text value is null, for example "No Job Title Specified".

Is this possible to do in knockout.js?

Thanks.

Andy White
  • 86,444
  • 48
  • 176
  • 211
Adam Levitt
  • 10,316
  • 26
  • 84
  • 145

7 Answers7

49

The shortest / easiest way is probably:

<label data-bind="text: JobTitle()||'No Job Title Specified'"></label>

Working example:

var ViewModel = function() {
    this.jobTitle = ko.observable();
};
 
ko.applyBindings(new ViewModel());
body { font-family: arial; font-size: 14px; }
.liveExample { padding: 1em; background-color: #EEEEDD; border: 1px solid #CCC; max-width: 655px; }
.liveExample input { font-family: Arial; }
.liveExample b { font-weight: bold; }
.liveExample p { margin-top: 0.9em; margin-bottom: 0.9em; }
.liveExample select[multiple] { width: 100%; height: 8em; }
.liveExample h2 { margin-top: 0.4em; font-weight: bold; font-size: 1.2em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class='liveExample'>   
    <p>Job Title: <input data-bind='value: jobTitle' /></p> 
    <h2 data-bind="text: jobTitle()||'No Job Title Specified'"></h2>  
</div>

Or JS Fiddle: http://jsfiddle.net/735qC/43/

rjmunro
  • 27,203
  • 20
  • 110
  • 132
18

So I'm guessing you want a real default value and not just a placeholder text. Here's a way to do that with an extender;

ko.extenders.defaultIfNull = function(target, defaultValue) {
    var result = ko.computed({
        read: target,
        write: function(newValue) {
            if (!newValue) {
                target(defaultValue);
            } else {
                target(newValue);
            }
        }
    });

    result(target());

    return result;
};

var viewModel = function() {
   this.someValue = ko.observable().extend({ defaultIfNull: "some default" });
};

ko.applyBindings(new viewModel());

http://jsfiddle.net/madcapnmckay/aTMpp/

Hope this helps.

madcapnmckay
  • 15,782
  • 6
  • 61
  • 78
  • so maybe it's just me, but this ONLY displays the "default" value... try changing the value to something and watch it not update in your fiddle. – Jason May 31 '12 at 23:19
  • here's a [fiddle](http://jsfiddle.net/edelman/aTMpp/2/). Why doesn't this say `foo` (or `hola`, even) – Jason May 31 '12 at 23:35
  • 2
    @Jason - You are totally right, I missed the second half the update on the if! I've corrected the code, here's your fiddle fixed http://jsfiddle.net/mUVvZ/1/. Good spot! – madcapnmckay Jun 01 '12 at 00:34
3

Even shorter than other code samples is the following:

ko.extenders.withDefault = function(target, defaultValue) {
    target.subscribe(function(input) {
        if(!input) target(defaultValue)
    });
    return target
};

With the initiation

ko.observable().extend({ withDefault: "some Default" })
K. Rohde
  • 9,439
  • 1
  • 31
  • 51
1

You can do it like this:

<label data-bind="text: JobTitle() != undefined && JobTitle() != null ? JobTitle() : 'No Job Title Specified'"></label>

With the undefined control, you can check whether you have that function or not when you are loading the page for the first time.

Bura Chuhadar
  • 3,653
  • 1
  • 14
  • 17
1

It might be useful to have a default value for display purposes only, instead of applying extenders which always return a default value.

With this, properties of your model remain empty, which is better for example when it comes to validation (checking for empty properties) or sending data back to the server (you might not want to send back this default value).

You could use the custom binding in the following example, which is based on the text binding, which renders a supplied default text or - if not supplied (you could adjust this to your needs of course):

ko.bindingHandlers['textWithDefault'] = {
    'init': function() {
      return { 'controlsDescendantBindings': true };
    },
    'update': function (element, valueAccessor) {
        var value, defaultValue;
        var options = ko.utils.unwrapObservable(valueAccessor());
        if (options !== null && typeof options == "object") {
            value = ko.unwrap(options['text']);
            defaultValue = ko.unwrap(options['default']);
        } else {
            value = options;
        }
        defaultValue = defaultValue || '-';

        ko.utils.setTextContent(element, value || defaultValue);
    }
};

function ExampleModel() {
  this.value = 'Sample text';
  this.observableValue = ko.observable('More sample text');
  this.emptyValue = '';
  this.emptyObservableValue = ko.observable();
};

ko.applyBindings(new ExampleModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
<div data-bind="textWithDefault: value"></div>
<div data-bind="textWithDefault: observableValue"></div>
<div data-bind="textWithDefault: emptyValue"></div>
<div data-bind="textWithDefault: emptyObservableValue"></div>
<div data-bind="textWithDefault: { text: emptyValue, default: 'Custom empty value' }"></div>
<div data-bind="textWithDefault: { text: emptyObservableValue, default: 'Another custom empty value' }"></div>
Philip Bijker
  • 4,955
  • 2
  • 36
  • 44
0

I ran into this when returning data for a table, but only having some columns sortable.

<tr data-bind="foreach: ArrAsrColumnHeaders">
  <th class="sortable koJson" data-bind="
     css: {active: ((typeof(isActive) != 'undefined') ?  isActive : '')}
      , text: text
      , attr:{href: ((typeof(jsonClick) != 'undefined') ?  jsonClick : '')}">
  </th>
</tr>

This says that, for each column in the table's header, add the class 'active' if 'isActive' is defined and set to true, but don't freak out if it isn't there. The same goes for adding a 'href' attribute.

Disclaimer: I don't understand knockout well enough to know the limitations of this approach, but this worked for me in my case, whereas, the more straightforward approach of css: {active: isActive() || ''} threw errors.

JJ Rohrer
  • 2,671
  • 5
  • 29
  • 37
0

To show the data in the input field, the easiest way is...

<input title="Stage" value="Default Entry Goes Here" readonly="readonly" type="text" id="stage" class="form-control" data-bind="value: model.stage ||text-input: Default Entry Goes Here">

It errors but it works.. :)

speed_racer
  • 45
  • 1
  • 9