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.
Related
I have a modal window used to update or add a new object Store.
This modal has a cascading dropdownlist for two properties: Department and District.
How should it work:
We first identify if we are in the situation of creating or updating a Store.
In case is a new Store the modal opens and using jQuery we present a default value for the District dropdownlists (since no Department has been chosen yet).
<script type="text/javascript">
var wasclicked = 0;
var $this = this;
$(document).ready(function () {
document.getElementById("modalbutton").onclick = function () {
//is AddNew Store button is hitted, this var = 1
wasclicked = 1;
};
$('#modal-action-store').on('hidden.bs.modal', function () {
//global.wasclicked = 0;
wasclicked = 0;
$(this).removeData('bs.modal');
});
$('#modal-action-store').on('shown.bs.modal', function (e) {
console.log($('#DistrictID').length);
//if wasclicked equals 1 that means we are in the AddNew Store scenario.
if (wasclicked == 1) {
//a default value is sent to District dropdownlist
var items = "<option value='0'>-- Seleccione Distrito --</option>";
$('#DistrictID').html(items);
};
});
});
</script>
However, at this moment, after the default value is added, after a fraction of a second, the value gets deleted.
What I've tried
I've noticed that when I remove this line of code:
$(this).removeData('bs.modal');
From the previous script, it works ok, but I need that code in order to clear the data from the modal if I need to use the modal to edit another Store.
Plus, when I debug the project the debugger did not stop at the breakpoint of that line, so I'm not sure why it's somehow executing in the background? Is it because it's wrapped inside the function document.ready()?
I've been struggling with this for days. I thank for any helpful comment.
Aditional info:
Online version of the project:
There is an online version for this for debugging:
http://plataformafantasypark.azurewebsites.net/
user: adelgado password: $Adelgado33
Under the menu 'Tiendas' -> 'Registro'
Button that calls the modal:
<div class="btn-group" id="modalbutton">
<a id="createEditStoreModal" data-toggle="modal" asp-action="Create"
data-target="#modal-action-store" class="btn btn-primary">
<i class="glyphicon glyphicon-plus"></i> NEW STORE
</a>
</div>
The Modal:
#model Application.Models.ApplicationviewModels.StoreIndexData
#using Application.Models
<form asp-action="Create" role="form">
#await Html.PartialAsync("_ModalHeader", new ModalHeader
{ Heading = String.Format("Actualización de Modelo: Tiendas") })
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="modal-body form-horizontal">
<div class="form-group">
<label asp-for="DepartmentID" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="DepartmentID" class="form-control"
asp-items="#(new SelectList(#ViewBag.ListofDepartment,"DepartmentID","DepartmentName"))"></select>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Distrito</label>
<div class="col-md-10">
<select class="form-control" id="DistrictID" name="DistrictID" asp-for="DistrictID"
asp-items="#(new SelectList(#ViewBag.ListofDistrict,"DistrictID","DistrictName"))"></select>
</div>
</div>
{... more elements}
</div>
</form>
Why not do something like(?):
$("#modalbutton").on('click', function () {
//is AddNew Store button is hitted, this var = 1
wasclicked = 1;
});
The idea is to take manual control of the loading and inserting of the remote content received from /Stores/Create. I'm not set up to test this but give it a try.
You will need to get rid of/comment out the existing .on('showN.bs.modal') handler, and you might have to get rid of the href on the <a> tag that invokes the modal, not sure.
$('#modal-action-store').on('show.bs.modal', function (e) {
if (wasclicked == 1) {
// load the form, once it's done loading modify it as needed
$('#modal-action-store').find('.modal-content').load('/Stores/Create', function () {
$('#DistrictID').html('<option value='0'>-- Seleccione Distrito --</option>');
});
}
});
This is what I have right now. I'm trying to add fields to the form dynamically using jQuery add() and append() method. But I want to remove the particular added field when the remove button is clicked.
<div class="col-md-12">
<h3>Added Description Fields</h3>
<div class="col-md-12" id="descFields">
</div>
</div>
JS
$(document).ready(function() {
console.log(descFields);
$('#addDesc').click(function(e) {
var descFields = $('#descFields');
var descLabel = $('#descLabel').val();
var large = '<div class="form-group" id="descField"><div class="input-group"><input type="text" class="form-control" placeholder="Enter Value For ' + descLabel + '" /><span class="input-group-btn"><button class="btn btn-danger" id="removeDesc" type="button">Remove</button></span></div>';
descFields.add(large).appendTo(descFields);
e.preventDefault();
});
$('#removeDesc').click(function(e) {
$(this).remove();
});
});
When the user click on the #removeDesc button , the the field that is added should be removed. I cannot figure out how to achieve this.
There are many ways of doing this, but the simpler for your problem is this one:
$(document).ready(function() {
console.log(descFields);
$('#addDesc').click(function(e) {
var descFields = $('#descFields');
var descLabel = $('#descLabel').val();
var large = '<div class="form-group" id="descField"><div class="input-group"><input type="text" class="form-control" placeholder="Enter Value For ' + descLabel + '" /><span class="input-group-btn"><button class="btn btn-danger" id="removeDesc" type="button">Remove</button></span></div>';
descFields.add(large).appendTo(descFields);
e.preventDefault();
});
$('#descFields').on('click', '#removeDesc', function(e) {
$(this).parents('.form-group').remove();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="descLabel"/>
<button id="addDesc">Add Desc</button>
<div class="col-md-12">
<h3>Added Description Fields</h3>
<div class="col-md-12" id="descFields">
</div>
</div>
Your problem is in the callback to delete the rows. When the document has finished loading you are trying to attach a click event to an object #removeDesc that is still not present in the DOM because it's created on the fly when the user clicks the #addDesc.
That's why you should use:
$('#descFields').on('click', '#removeDesc', function(e) {
$(this).parents('.form-group').remove();
});
As #vijayP suggested before you can use the on() to attach an event handler to the container where you'll be adding the object that is still not present in the DOM. Then you pass in the query selector as the second parameter to filter in execution time which of its children will trigger the event and execute the callback.
My additional trick is that I'm using .parents('.form-group') to select the div containing the group and remove all of the fields that were added instead of removing only the button.
Happy coding!
Add click event for remove button like follows:
$(document).on("click","#removeDesc",function(e) {
$(this).remove();
});
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.
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.
I have the following function that is meant to get called after a form I submitted (this method is in my application.js folder in my rails app):
var addToTopics = function() {
var result = "";
var checkedTopics = $(".topic-checkbox:checked");
$.each(checkedTopics, function(i, topic) {
if(i == 0) {
result = result + $(topic).attr('value');
}
else {
result = result + ", " + $(topic).attr('value');
}
});
return result;
};
$("#new_comment").submit(function() {
var ListOfTopics = addToTopics();
$('#comment_topics').val(ListOfTopics);
alert($('#comment_topics').val());
return true;
});
HTML
<form method="post" id="new_comment" class="new_comment" action="/comments" accept-charset="UTF-8"><div style="margin:0;padding:0;display:inline"><input type="hidden" value="✓" name="utf8"><input type="hidden" value="8vLhtuco+TAkeB+9kQ0gERvA54BD/BnjJuguWxuXWHQ=" name="authenticity_token"></div>
<div class="field">
<label for="comment_comment">Comment</label>
<br>
<textarea rows="20" name="comment[comment]" id="comment_comment" cols="40"></textarea>
</div>
<div class="field">
<input type="hidden" value="28" name="comment[review_id]" id="comment_review_id">
</div>
<div class="field">
<input type="hidden" value="1" name="comment[user_id]" id="comment_user_id">
</div>
<div class="field">
<input type="hidden" value="" name="comment[topics]" id="comment_topics">
</div>
<div class="actions">
<input type="submit" value="Create Comment" name="commit" id="comment_submit">
</div>
</form>
But this dosen't seem to be getting called as the alert dosent even go off when I submit my form.
Any idea why? Thanks
You need to ensure the code attaching the listener runs after the form has been rendered to the dom. this is one way of doing it:
$(function() {
$("#new_comment").submit(function() {
var ListOfTopics = addToTopics();
$('#comment_topics').val(ListOfTopics);
alert($('#comment_topics').val());
return true;
});
});
has an element with the id new_comment been added to the dom prior to you running $("#new_comment").submit(function(){...});
If not, then $("#new_comment") will return no selections and jquery will silently 'fail'.
i had this same issue yesterday, my issue was an unrelated javascript error on the page
add a simple alert above this line
$("#new_comment").submit(function() {
to check (or check your browser js console)
Try changing $("#new_comment").submit(function() to $("#comment_form").submit(function(), from what I'm seeing in your code those two selectors are not matching. Hope it helps. Cheers
And since you have an action attribute in your form "action='/Comment'" either remove it if you do not have a valid reason why you have it there or prevent the default behavior which will force the form to redirect to your current url + /Comment
So the code will look like :
$("#comment_form").submit(function(evt) {
evt.preventDefault();
// rest of function below
});