Passing form attributes into partials - javascript

I am working on an app with NodeJS and have been able to use handlebars and partials without much trouble. I am getting to the point where I have view and edit forms for a car.
For example, after the user submits an application I have "View" and "Edit" links that go to localhost:3000/cars/:id/view and localhost:3000/cars/:id/edit, respectively.
The only difference between these two links is that the "View" page has the form with readonly="readonly" and the "Edit" does not have the readonly attribute.
What I would like to do
cars/view
{{ >car_form readonly=true }}
cars/edit
{{ >car_form readonly=false }}
<button type="submit">Submit</button>
Is this possible with handlebars templates? Or is there something similar I can do to get the result I want?
Thank you!

You're passing in the readonly option correctly, now all you need to do is use it when rendering your form.
To do that I've used a helper to render the form via JavaScript, where we can do anything we like using the Hash Arguments funtionality Handlebars supports.
// Let's pretend this is the user input you're holding in Node.js
// and want to render in your view and edit forms.
var userInput = {
"name": "Bob",
"tagline": "Yep, I'm Bob!"
};
Handlebars.registerHelper('userForm', function(options) {
var formOpening = options.hash.readonly ? "<form readonly=\"readonly\">" : "<form>";
// Notice that I feed in `userInput` as the context here.
// You may need to do this differently, depending on your setup.
var formContents = options.fn(userInput);
return formOpening + formContents + "</form>";
});
var userFormTemplate = Handlebars.compile(document.getElementById("userForm-template").innerHTML);
var pageTemplate = Handlebars.compile(document.getElementById("page-template").innerHTML);
Handlebars.registerPartial('userForm', userFormTemplate);
document.getElementById("wrap").innerHTML = pageTemplate();
<div id="wrap"></div>
<script id="page-template" type="text/x-handlebars-template">
<h2>View</h2>
{{> userForm readonly=true}}
<h2>Edit</h2>
{{> userForm readonly=false}}
</script>
<script id="userForm-template" type="text/x-handlebars-template">
{{#userForm readonly=readonly}}
<input type="text" name="name" value="{{name}}" />
<input type="text" name="tagline" value="{{tagline}}" />
<button type="submit">Submit</button>
{{/userForm}}
</script>
<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v4.0.5.js"></script>
In the above snippet, the readonly="readonly" attribute is being added to the form beneath the "View" heading.
Note that on my browser, this does not actually make the form read-only - I'm not sure if you've got another library or something to handle that?

Related

MeteorJS.On refreshing a page the search results get lost

I am using a searchbox for my webapp.On clicking the search button the user gets directed to the 'searchresults' page and the results are displayed.But on refreshing the page the search results are lost.How can I recover them using session?
The HTML code is:
<div class="form-group">
<input type="text" class="form-control" id='searchbox' placeholder="Search">
</div>
The js code is:
Template.navigation.events({
'submit form':function(event){
event.preventDefault();
var searchbox=document.getElementById('searchbox').value;
Router.go('/posts/search/'+searchbox);
}
});
Template.searchresults.helpers({
'post':function(){
var searchbox=document.getElementById('searchbox').value;
var search=new RegExp('\\b'+searchbox+'\\b','i');
return Posts.find({name:search});
}
});
and routerjs code is:
Router.route('/posts/search/:somesearch/',{
template:'searchresults',
name:'searchresults',
});
Instead of using a helper function, let's set the data context in your route:
Router.route('/posts/search/:somesearch/',{
template:'searchresults',
name:'searchresults',
data: function(){
return Posts.find({ name: this.params.somesearch });
}
});
Remove your post template helper entirely.
In your HTML template you'll need to iterate with {{#each this}} as this will be the cursor returned by the data function in the router.
Note, for multi-word searches and special characters you'll need to url-encode/decode the search string.

jQuery - replaceWith to update HTML does not make image or link

I am writing a small code with JavaScript (using jQuery and Knockout) and HTML that takes user input (GitHub username), checks if the input is valid against a GitHub api, and displays the user's GitHub avatar and username (linked to the matching profile on GitHub). The display replaces the form in which the user entered the username.The original HTML before the user inputs is:
<div id="inputSection">
<form>
<p>
GitHub Username:
<input type="text" name="username" value="" placeholder="username" id="un"/>
<button type="button" id="submitButton">Login</button>
</p>
</form>
</div>
And the code to replace it is this:
$("#submitButton").click(function() {
var username = document.getElementById('un').value;
var inputForm = $(document.getElementById('inputSection'));
$.ajax( {
...
success: function () {
alert("Welcome, " + username);
var userURL = 'https://github.com/' + username;
var inputContent = $('<a data-bind="attr: {href: userURL}"><img data-bind="attr: {src: avatar_url}" height=\"30\" width=\"30\"/>' + username + '</a>');
$(inputForm.replaceWith(inputContent));
}
});
});
It seems to work for the most part. After the alert welcomes the user by username, the form disappears from the webpage. It is replaced by the username, which is formatted like a link. However, it does not function as one. Clicking it does not do anything. Also, the user's avatar, despite showing a box of the set size on the webpage, does not appear.The solution is likely very simple and obvious, but as I only started learning these languages and libraries this week, I am not sure what is going wrong. Knockout should be running on the HTML page that calls the JavaScript page, and the ajax is working with regards to other functions, so I assume that's fine. The value "avatar_url" is a part of the api requested with ajax at https://api.github.com/users.I've tried all sorts of different things to no effect. If you want any more information or have a suggestion to make this question better, please comment. I'm new to coding and Stack Overflow, but I want to make both my programs and my questions as good as possible. Thank you for your time.
EDIT: 1. I originally failed to set a size for the image, resulting in a 0x0 image. This has been corrected, though the image itself still does not display. 2. When I first put in my code, I tried to make it easier to read by excluding where some variables had been renamed for other, unrelated portions and just making all the names match between the two relevant snippets. I did not catch them all. They should all match now.
Short answer:
You’re inserting an html element with data-binds without explicitly initializing its bindings. Use ko.applyBindings(vm, node) on the newly injected part of the DOM.
Long answer:
If you're new to coding and to both jQuery and knockout, I'd suggest not using both libraries at once. Here’s why:
If you want to use knockout, you'll have to stick to a certain kind of software architecture:
Simplify dynamic JavaScript UIs with the Model-View-View Model (MVVM) (http://knockoutjs.com/)
jQuery, on the other hand, is more of a toolbox. It doesn't dictate an architectural pattern.
It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers. (https://jquery.com/)
This might sound a bit lame, and isn't really an answer, but I'll show you the differences between solving your problem the “knockout way”, and “the jQuery way”. I’ll start with the latter, since it’s closest to your current approach:
The jQuery approach (note that I'm skipping the Ajax part)
Find the elements you need to make your UI interactive. Attach event listeners to buttons, modify the DOM when new data is available.
$(document).ready(function() {
// Notice that you don't need document.getElementById
var submitButton = $("#loginButton");
var userNameInput = $("#un");
var inputSection = $("#inputSection");
var getContentString = function(userName) {
var userUrl = "https://github.com/" + userName;
var avatarUrl = "...";
// Inject the user specific attributes
return "<a href=`" + userUrl + "`><img src=`" + avatarUrl + "` height='30' width='30'/>" + userName + "</a>";
};
var onSubmitClick = function(event) {
var userName = userNameInput.val();
var onSuccess = function() {
// Create new <a> element and replace the form with the new HTML
var inputContent = $(getContentString(userName));
inputSection.replaceWith(inputContent);
};
/*
$.ajax({
success: onSuccess
});
*/
//Just call onSuccess to circumvent unimplemented ajax:
onSuccess();
};
submitButton.click(onSubmitClick);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="inputSection">
<p>
GitHub Username:
<input type="text" name="username" value="" placeholder="username" id="un" />
<button type="button" id="loginButton">Login</button>
</p>
</form>
The knockout approach
Create a viewmodel for your user. Bind the input and compute the other properties automatically. Attach event listeners through data-binds. Use if, visible or template bindings to swap out parts of the UI.
var UserViewModel = function() {
this.userName = ko.observable("");
this.confirmed = ko.observable(false);
this.userUrl = ko.computed(function() {
return "https://github.com/" + this.userName();
}, this);
this.avatarUrl = ko.computed(function() {
return "???" + this.userName();
}, this);
};
UserViewModel.prototype.confirm = function() {
/* ajax (disabled for example)
$.ajax({
success: this.confirmed.bind(null, true)
});
*/
this.confirmed(true);
};
var viewModel = {
user: new UserViewModel()
};
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="with: user">
<!-- ko ifnot: confirmed -->
<form>
<p>
GitHub Username:
<input data-bind="value: userName" type="text" placeholder="username" />
<button data-bind="click: confirm">Login</button>
</p>
</form>
<!-- /ko -->
<!-- ko if: confirmed -->
<a data-bind="attr: { href: userUrl }">
<img data-bind="attr: {src: avatarUrl }" />
<span data-bind="text: userName"></span>
</a>
<!-- /ko -->
</div>
With help from user3297291's jQuery answer above, I eventually came to this. The answer was good and necessary for this progression; some parts just did not work for this situation (mostly simple compatibility issues with other code not included in this sample). Though this is a very specific question, I thought the solution should be included. Note that I have decided to stay away from Knockout for now.
The HTML suggestion to attach the id to the form rather than the div is a good move.
$("#submitButton").click(function inputForm() {
var username = $("#un").val();
function makeUserContent(user, profile, avatar) { //arguments are data from ajax
//writes a string without the messy quotes within quotes within quotes problem
//much clearer than trying to handle the jQuery and string all at once
return "<img src=" + avatar + " height='30' width='30' />" + user + "";
}
function submitUsername() {
$.ajax({
...
success: function correntInformation(data) {
//data is what the ajax gets, which is passed for use
alert("Welcome, " + username + ".");
//calls to make the string with the data gotten by ajax
var inputContent = $(makeUserContent(data.login, data.html_url, data.avatar_url));
$("#inputSection").replaceWith(inputContent);
}
})
}
submitUsername();
})
The biggest things I took away from this problem is this: simplify strings, hold and use data, work with one library at a time (until experienced with both).

Get the value of an <input type="text"> that was created dynamically inside a modal

i would like to get the value of an <input type="text"> that was created dynamically inside a modal and put it into variable "newcomment".
This is how i make the input:
var newcomment;
var p = $("<p>");
p.append("Some text");
p.append("</p>");
p.append("<input type='text'id='comment_text' value='Comment'"+"onblur=newcomment=$('#comment_text').val()"+" onfocus=if(this.value==this.defaultValue)this.value=''>"+"</input>");
$("div.modal-body").append(p);
The problem is when i write something like "ok" inside the textbox in the modal, and after i focusout from the textbox: newcomment seems not update to "ok" and still have the default "Comment" value.
1st: You need to use newcomment=this.value instead of newcomment=$('#comment_text').val()
2nd: No need to add + signs in your input html code while you not trying to concatenate string by putting variables to it
var newcomment;
var p = $("<p>");
p.append("Some text");
p.append("</p>");
p.append("<input type='text' id='comment_text' value='Comment' onblur='newcomment=this.value; alert(newcomment);' onfocus=if(this.value==this.defaultValue)this.value='' />");
$("body").append(p);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Not really the answer here, but might help you get at the root of the problem.
JS:
var newComment, defaultValue;
function doOnBlur(){
newComment = $('#comment_text').val()
}
function doOnFocus(){
if($('#comment_text').val() == defaultValue){
$('#comment_text').val('')
}
}
HTML:
<input type='text' id='comment_text' placeholder='Comment' onblur='doOnBlur()' onfocus='doOnFocus()' />
<!-- inputs dont have a close tag, also should use placeholder for temporary text -->
from here, you can set breakpoints in the debugger and see where your code is going wrong. You can also modify the functions much more easily rather than writing the executing code in the HTML

How to pass JavaScript variable to g:remoteFunction "update" property?

I have a function in JavaScript that submits a message to a method in a Grails controller and at the same time updates div with myID id.
function messageKeyPress(field,event,messageBox) {
...
var message = $('#messageBox').val();
<g:remoteFunction action="submitMessage" params="\'message=\'+message" update="myID"/>
...
}
I use it like this:
<div id="chatMessages" class="chatMessages"></div>
<input type="text" id="messageBox" class="messageBox" name="message" onkeypress="messageKeyPress(this,event,'#messageBox');"/>
<div id="myID">
I would like that function to be reusable being able to update different divs.
I tried:
onkeypress="messageKeyPress(this,event,'#messageBox', '#myID');"
and in JavaScript:
function messageKeyPress(field,event,messageBox, myID) {
...
<g:remoteFunction action="submitMessage" params="\'message=\'+message" update="${myID}"/>
But that didn't work. My question is how to pass a JavaScript variable to Grails g:remoteFunction "update" property.
I suggest you to use jQuery instead. It is bundled by default to Grails projects. As a result, you'll get a neat separation between javascript code and gsp view logic. For instance, application.js might look like this:
(function($) {
$('.messageBox').on('keypress', function () {
...
var params = {message: $(this).val()};
var url = $(this).data('url');
var target = $(this).data('target');
$.post(url, params, function(response) {
$(target).html(response);
});
...
});
})(jQuery);
and your view file:
<input type="text" id="messageBox"
class="messageBox" name="message"
data-url="${createLink(action: 'submitMessage')}"
data-target="#myId"/>
<div id="myID"></div>
You should assign a messageBox css class to every input field you want to have this event listener. And in data-target attribute of every field you can specify a selector for all divs that should be updated.
jQuery is very easy to learn. http://api.jquery.com/
The update attribute should be set to the ID of the element to be updated, not a selector that matches this element. In other words, try this:
onkeypress="messageKeyPress(this,event,'#messageBox', 'myID');" // '#' removed from myID

Dynamically duplicated forms disappear on CodeIgniter reload

I have the following code that needs to be duplicated:
<form method="post">
<div id="field-row-container">
<div id="field-row-1" class="field-row">
<div class="field-element">
<label for="Name[1]">Name</label>
<input type="text" id="Name[1]" name="Name[]" />
</div>
<div class="field-element">
<label for="Email[1]">Email</label>
<input type="text" id="Email[1]" name="Email[]" />
</div>
<hr/>
</div>
</div>
<div class="form-element">
<input type="button" class="confirm add-field-row" value="Add" />
<input type="button" class="danger delete-field-row" value="Delete" />
<input type="submit" />
</div>
The duplicated / dynamically added elements will have the same names of Name[] and Email[] but their ID's will be incremented.
The JavaScript is below, based from Josiah Ruddell's form duplication script.
var template = $('#field-row-container #field-row-1').clone();
window.addForm = function () {
var parent = $(this).closest('.dynamic-rows').attr('id');
var fieldRowCount = countRows(parent) + 1;
var newFieldRow = template.clone().find(':input').each(function () {
var newId = this.id.substring(0, this.id.length - 3) + "[" + fieldRowCount + "]";
$(this).prev().attr('for', newId); // update label for
this.id = newId; // update id and name (assume the same)
$(this).closest('.field-row').addClass('new-row');
}).end()
.attr('id', 'field-row-' + fieldRowCount)
.appendTo('#field-row-container');
}
$('.add-field-row').click(addForm);
Whenever I submit the form to CodeIgniter and if there is a validation error, once the controller reloads the multi-part form, the dynamically added elements disappear as well as the values in the initial fields.
How do I go around this problem? I'm at a loss on how to solve this...
Other notes that might help:
This is a component multi-part form with only one form controller
I have multiple instances of this - Addresses, Education Degrees and such
I use CodeIgniter's form_validation library to check server-side each array of posts
When the page with the form on reloads after the controllers redirects back to it after failing validation, it will only reload the original page, with no DOM manipulations applied.
I would perform the POST request which submits the form via ajax, so you can handle the response without leaving the page. Something like this:
$.post('/locationOfController.php', $('#yourForm').serialize(), function(response){
if(response.valid){
window.location.href = '/somewhereAfterFormPosted.php';
} else {
$('#yourForm').append("<p>"+response.error+"</p>");
}
}, 'json');
and change the controller to return a JSON object based on whether validation passed or not. To match up with my example above, you would return something like below when an error occurs:
{valid: false, error: 'Please fill out the whole form'}
Try something like that as a basic example. You could do much more, such as returning several errors if multiple fields are invalid.

Categories

Resources