Ability to dynamically hide / show elements on a page using knockout js - javascript

EDIT: I updated my binding based on the feedback.
I have a sub page on my web application wherein I need the ability to dynamically hide / show elements on that page based on user selected filtering options. In order to do so, I have written a custom binding like this:
ko.bindingHandlers.customVisible = {
update: function(element, valueAccessor){
var params = ko.utils.unwrapObservable(valueAccessor());
var show = params.method(params.data, params.searchText());
ko.bindingHandlers.visible.update(element, function() { return show; });
}
};
The html markup for it is like this:
<div class="form-group" data-bind="foreach: objects">
<div class="col-md-9 col-sm-7 col-xs-12" data-bind="customVisible: {data: $data, method: $parent.filterSyncedObjects, searchText: $parent.searchText}">
</div>
</div>
Here is the markup of the input box:
<input type="search" data-bind="value: searchText, valueUpdate: 'keyup'" placeholder="Enter A name to search">
'filterSearchObjects' is the method that returns true or false depending on whether the search matches and 'searchText' is an observable containing the user entered text. Here is my question:
Why isn't the customVisible.update function being fired when the value of 'searchText' changes?
Any help will be greatly appreciated.

Related

append has no effect on a form based on Backbone.js

I retrieved a template Javascript / HTML based on Backbone.js. I would need to add a dynamic dropdown list in a field of the following form:
<script type="text/template" id="compose-view-template">
<form id="email-compose" class="form-email-compose" method="get" action="">
<div class="form-group">
<select type="email" id="input-to" placeholder="To" class="input-transparent form-control">
</select>
</div>
<div class="form-group">
<input type="text" id="input-subject" placeholder="Subject" class="input-transparent form-control"
value="<%= subject %>">
</div>
<div class="form-group">
<textarea rows="10" class="form-control" id="wysiwyg" placeholder="Message"><%- body %></textarea>
</div>
<div class="clearfix">
<div class="btn-toolbar pull-xs-right">
<button type="reset" id="compose-discard-button" class="btn btn-gray">Annuler</button>
<button type="submit" id="compose-send-button" onClick="fillEmailDropDown()" class="btn btn-danger"> Envoyer </button>
</div>
</div>
</form>
</script>
The Backbone part is the following:
var ComposeView = Backbone.View.extend({
template: _.template($('#compose-view-template').html()),
attributes: {
id: 'compose-view',
class: 'compose-view'
},
events: {
"click #compose-save-button, #compose-send-button, #compose-discard-button": 'backToFolders'
},
render: function() {
$('#widget-email-header').html(
'<h5>Nouvel <span class="fw-semi-bold">Email</span></h5>'
);
$('#folder-stats').addClass('hide');
$('#back-btn').removeClass('hide');
this.$el.html(this.template(this.model.toJSON()));
this._initViewComponents();
return this;
},
backToFolders: function(){
App.showEmailsView();
},
_initViewComponents: function(){
this.$("textarea").wysihtml5({
html: true,
customTemplates: bs3Wysihtml5Templates,
stylesheets: []
});
}
});
The Javascript is the following:
$(document).ready(function() {
var listItems = '<option selected="selected" value="0">- Select -</option>';
console.log("Preparing Auto Fill of DropDown list for email adresses");
for (var i = 0; i < jsonData.Table.length; i++) {
listItems += "<option value='" + jsonData.Table[i].stateid + "'>" + jsonData.Table[i].statename + "</option>";
}
$("#input-to").append(listItems);
});
Unfortunately the .append has no effect on the select form, and the Dropdown remains empty.
I also tried to use .html instead of append but same result.
If I'm adding the options manually in the select tag, it works properly but I need to fill it dynamically...
any idea ?
It sounds like the issue is likely that backbone hasn't added the #input-to element to your html before you attempt to append to it. That wouldn't throw any errors, it would just silently fail, then backbone would add the (empty) element after the append attempt.
You only know it is "safe" to append your content after backbone has added the html, and that happens in your render method:
render: function() {
$('#widget-email-header').html(
'<h5>Nouvel <span class="fw-semi-bold">Email</span></h5>'
);
$('#folder-stats').addClass('hide');
$('#back-btn').removeClass('hide');
this.$el.html(this.template(this.model.toJSON()));
// NOW IT IS SAFE TO APPEND CONTENT...
this._initViewComponents();
return this;
},
A solution that will definitely work is to add you code that appends to the element directly in the render method. I'd recommend that if the appended content is very intrinsic to the view.
If that doesn't seem coherent, then a lot will depend on how your backbone is set up. It's probably safe to append the content after the line that initializes your view:
var myView = new ComposeView({});
// NOW APPEND CONTENT
However, that will only be safe if:
Your view is instantly rendered when created, and
The render is synchronous (does not fetch external data or templates first).
Hope this helps, good luck.

Get the value of an element inside ng-repeat

Here is my html snippet:
<tr ng-repeat="row in rowCollection">
<td><h6 id="{{$index}}">{{row.inventory_serialno}}</h6></td>
</tr>
<div class="form-group">
<label for="inventory_serialno">SL No:</label>
<input class="form-control" value="//I need the value of h6 here//" id="inventory_serialno" placeholder="Enter serial number">
</div>
Goal:
Get the value of an element inside ng-repeat (In this case the 'h6' tag value of each row)
Use that corresponding value to autofill the value of respective text box input present in each row.
The input text box is out of the ng-repeat scope.
For some reason i cant access the value of the h6 tag if i do this (gives a value null)
document.getElementById("1").innerHTML; //or even text()
Weirdly i noticed the above JavaScript code runs fine on the browser console but not on my script (which means its something to do with the angular element generation time.. May be i'm accessing it before it even got generated).
Nonetheless can someone please shed some light on the solution. Thanks in advance.
******** Updated with controller code *****
app.controller('InventoryCtrl', function ($scope) {
var x = document.getElementById("1").innerHTML; // giving a null value
console.log(x);
$scope.rowCollection = [{
id: 100,
inventory_serialno: 'HW13FGL'
}, {
id: 101,
inventory_serialno: 'SM123GH'
}, {
id: 102,
inventory_serialno: 'LK90100'
}];
});
You could do it like this:
<input value="{{rowCollection[2].inventory_serialno}}" />
Example
However I would place the input inside the ng-repeat to keep it clean, and achieve the layout you like inside a big wrapper, something like this:
<div class="grid" ng-repeat="row in rowCollection">
<div class="col">
<h6 id="{{$index}}">{{row.inventory_serialno}}</h6>
<hr>
<div class="form-group">
<form>
<input value="{{row.inventory_serialno}}"/>
</form>
</div>
</div>
</div>
If you need an input for each item, use ng-repeat.

parsley 2.0.3 not working with boostrap 3

i am trying to use parsely.js on my html page to validate input box. currently this html page contains one input box and one submit button. the structure is created using bootstrap 3 and this page does not contain Form tag.
<div role='form'>
<div class="row form-group">
<div class="col-xs-3">
<label title="fullname">Full Name</label>
</div>
<div class="col-xs-4">
<input type="text" class='form-control' id="name" name="fullName" data-parsley-required="true" data-parsley-required-message="Please insert your name"/>
</div>
</div>
<input type="submit" class= "btn btn-danger"/> </div>
i am calling parsley.js like
function validateInput()
{
var handle = $("input[name='fullName']").parsley({
successClass: "has-success",
errorClass: "has-error",
classHandler: function (el) {
return $(el).closest('.form-group');//not working
},
errorsWrapper: "<span class='help-block'></span>",
errorTemplate: "<span></span>",
});
return handle.isValid();
}
on click of Submit button. it returns true/false correctly and create span tag also. but error classes are not applied. even data-parsley-required-message'Please insert your name' is not working.
when i put alert($(el)) or alert(el) it gives [object Object]. i think el should be the input object on which i am calling parsley function. but i am not able to get el.attr('id') or any other attribute. it returns undefined. i have also tried
//return el.closest('.form-group');//not working
//return el.$element.closest('.form-group)//not working
//return $(el).$element.closest('.form-group')//not working
I can not use Form tag as i am using this html structure in sharepoint content edtior web part.
A few things first:
Parsley allows you to bind it to a field, so you won't have a problem without the form element (see docs);
The classHandler function recieves an object of the type ParsleyField. With this object, you can access the input element with el.$element (for example: alert(el.$element.attr('id'));
I have made the following changes to your validateInput function:
<script type="text/javascript">
function validateInput() {
$("input[name='fullName']").parsley({
successClass: "has-success",
errorClass: "has-error",
classHandler: function (el) {
return el.$element.closest('.form-group'); //working
},
errorsWrapper: "<span class='help-block'></span>",
errorTemplate: "<span></span>",
});
// Returns true / false if the field has been validated. Does not affect UI.
//$("input[name='fullName']").parsley().isValid());
// validate field and affects UI
$("input[name='fullName']").parsley().validate();
}
</script>
With this code, the message is presented correctly, and the successClass and errorClass are appended to the div form-group.
See the following working jsfiddle

jQuery .val('xxx') not setting value, but .attr('value', 'xxx') is?

I have a table which is populated with javascript, fetching data via ajax. I have a <tr> template which I clone and set the values within to the data retrieved via the ajax request. I'm using Twitter bootstrap's popovers, so that users can click on a cell, which pops the popover with an input/select etc prompting the user to change the cell value. The problem occurs when I am setting the value of those inputs. When I am building each row, I set the value of the input in the popover template with .val('xxx'), but when I log it/click the cell and view the popover, nothing is changed; conversely, if I set it with .attr('value', 'xxx'), it works just fine.. why would .val('xxx') not work?
Here's what the meat of it looks like..
Row template:
....
<td>
<div class="last-name popover-toggle">
<span class="vlabel">----</span>
<div class="popout">
<input type="text" name="last_name" value=""/>
<div class="popout-button-container">
<button type="button" class="btn btn-primary btn-small cancel-field">Cancel</button>
<button data-url="{{ url('edit_lead.json') }}" type="button" class="btn btn-primary btn-small save-field">Save</button>
</div>
</div>
</div>
</td>
....
Setting input value:
...
if (data['last_name']) {
$nRow.find('.last-name input[name=last_name]').val(data['last_name']);//.attr('value', data['last_name']);
$nRow.find('.last-name .vlabel').text(data['last_name']);
}
registerPopover($nRow.find('.last-name'), 'Last Name');
....
Register popover:
function registerPopover(el, title) {
var options = {
animation: false,
content: el.find('.popout').html(),
html: true,
trigger: 'manual'
};
if (typeof(title) != 'undefined') {
options['title'] = title;
}
el.popover(options);
}
Not sure what is your actual problem, question is not very obvious to me but there is a difference between attr and val. The attr method sets/change the attribute of an input/element in the source and you may see it using browser's inspection tool, on the other hand, val method sets/update the rendered property which is in the memory but it doesn't effect the original source/HTML in the browser's source code. For example, if you do something like this
HTML:
<input type = "text" name="txt" id = "txt" value = "Hello!" />
JS:
$('#txt').val('World!').clone().insertAfter($('#txt'));
And check the source code, then you'll see there is two input elements and both have value attribute Hello! but on the screen both have world!. Check this detailed answer.

knockout validation - at least one field has a value and at least one checkbox checked

I'm trying to do some very simple validation using the knockout validation plugin. I want to validate if at least one text field has text and at least one checkbox is checked. All bindings work correctly and knockout itself is awesome so far. I've tested native validation rules and they work with messaging. I just can't get the validation to work for these 2 rules.
I realize I can check for empty values very easily with jQuery but I would really like to utilize knockout.
The model (without validation because I haven't found anything that works yet):
var SearchForm = function(collections) {
// main search fields
this.fullRecord = ko.observable();
this.title = ko.observable();
this.author = ko.observable();
// collections to search
var sources = [];
$.each(collections, function(index,collection) {
sources.push(new Source(collection));
});
this.sources = ko.observableArray(sources);
// Error handling vars
this.errors = ko.validation.group(this);
};
var Source = function(collection) {
$.extend(this,collection);
this.id = "collection-"+this.code;
this.selected = ko.observable(true);
};
Here I'm just creating a list of source objects from collection data that comes from the server. That data is irrelevant since I'm only concerned with the observable 'selected' property.
The markup:
<div id="advanced-controls" class="row">
<div class="col-sm-8">
<fieldset id="search-fields">
<div class="form-group">
<label for="fullrecord" class="control-label">Keywords:</label>
<input type="text" id="fullrecord" class="form-control" name="fullrecord" placeholder="Full Record Search" data-bind="value:fullRecord" />
</div>
<div class="form-group">
<label for="title" class="control-label">Title:</label>
<input type="text" id="title" name="title" class="form-control" data-bind="value:title"/>
</div>
<div class="form-group">
<label for="author" class="control-label">Author:</label>
<input type="text" id="author" name="author" class="form-control" data-bind="value:author"/>
</div>
<div class="form-group">
<button id="advanced-search-submit" class="btn btn-primary" data-bind="click:search">Search</button>
<button id="advanced-search-reset" class="btn" data-bind="click: clear">Clear All</button>
</div>
</fieldset>
</div>
<div class="col-sm-4">
<fieldset data-bind="foreach: sources">
<div class="form-group">
<input type="checkbox" name="collections" data-bind="attr:{ id:id, value:code }, checked:selected, click: $parent.clearRequiredSourceError ">
<label data-bind="attr:{ for:id }, text: name"></label>
</div>
</fieldset>
</div>
</div>
In the validation function before submitting:
// If there's any knockout validation errors
if (model.errors().length > 0) {
model.errors.showAllMessages();
isValid = false;
}
I've tried setting a custom validation extension on the observable array of sources like this:
this.sources = ko.observableArray(sources).extend({
validation: {
validator : function (sources) {
var anySelected = false;
$(sources).each(function(){
anySelected = this.selected();
});
return anySelected;
},
message: 'At least one source is required to search.'
}
});
But that doesn't fire when the checkboxes are clicked, only when the array is changed ~ push, pop, etc. Yes I have the config set correctly:
ko.validation.configure({
grouping: {
deep: true,
observable: true
}
});
This seems like it should be very simple to achieve. Maybe my brain is just fried from diving into the whole knockout world this week. Any suggestions are greatly appreciated. Thanks in advance!
Forgive me for not reading your entire question, as it is very long, but I am curious if you need Knockout validation for this or if you are looking for something like this -
var selectedOption = ko.observable();
var selectionsOk = ko.computed(function () {
((!!field1()|| !!field1()|| !!field1())&&!!selectedOption())
});
Where selectedOption is a list of radio buttons, and once one is selected returns the value, and you could either use an observableArray to contain each of your fields so it is dynamic or you list the fields out and make sure that at least one of them has a value. The !! will evaluate your observable as true or false, true would be returned unless the observables' value was null, undefined, '', or false
The selectionOk computed could be used to prevent clicking some button to proceed or inversely for displaying an error message until the conditions are met.

Categories