I'm building a dynamic form. It looks like this:
When "Add destination" is clicked I want to add another section, "Address 3". If "Remove destination" is clicked after that, "Address 3" should be renumbered to "Address 2".
I've done things like this before, but it always feels like a big mess of HTML and JavaScript. I'm trying to figure out the best approach. Here are some possible solutions:
Wrap each section in a <div>. When "Add destination" is clicked, clone the last section. Loop over each input in the clone, update the name and id attributes. Wrap the address # in a <span> and update that too. Add some more JavaScript to initialize the date and time pickers and validator.
Build the entire DOM with JavasScript/jQuery.
Put the entire chunk of HTML into a JavaScript string. Use placeholders for IDs, names, and numbers. Use some basic string replacing to increment them as necessary.
Use a client-side templating language like Jade that will only be used in one or two places throughout the entire project.
None of the solutions are particularly appealing. How would you approach this problem and why?
this is a quick hack but reliable in most conditions
http://jsfiddle.net/techsin/7pceP/
var s = $('.sample'), c= $('.container'), total=1;
$('.Add>a').click(function(){addSample();});
function addSample() {
var clone = s.first().clone();
clone.find('.Remove').toggle().click(function(){ rmvSample($(this)); });
clone.find('.num').text(++total);
clone.insertBefore(c.children(":last-child"));
}
function rmvSample(x) {
total--;
x.parent().remove();
updateIndex();
}
function updateIndex(){
$('.sample').each(function(i){
$(this).find('.num').text(i+1);
});
}
How: You could initialize a global var with value 1 and each time the add click handler is called, the variable is incremented and the name is formed by "Address " + globalCount. Similarly, when the remove destination is clicked, the global count is decremented.
Why: Very little logic, although global variables are not the greatest at times, it reduces other complexity that would arise from other solutions and is pretty elegant.
Other option: When creating the first destination section you could provide it with a data attribute $(destinationToCreate).data('count', 1).
I would assume that these destinations sections would be created within some kind of container and that they are of the same structure so that when you're creating a new one something like this would be possible:
var newCount = $('#destinationContainer div').last().data('count')++
So, what I've done is stuff an entire Twig template into a JavaScript string:
var template = {% filter json_encode|raw %}{% include 'bookings/address.twig' %}{% endfilter %};
The template contains some Underscore/lodash template variables in it, like <%- index %>, which I use in all the IDs and names. Then I render it out as needed:
_.template(template,{'index':addressCounter++})
This way I can take full advantage of the Twig templating language and use its form builder inside my template. I had briefly contemplated using Nunjucks instead of lodash, which is basically just Twig for JavaScript. I figured it might get confusing to use Twig inside of Twig, however, and I didn't want to include a full runtime when all I really need use a few simple variable substitutions.
Seems to be working pretty well so far.
Related
After months of web-development, I find myself completely helpless trying to find a good solution for a simple problem of formatting all the numbers throughout the DOM as I wish. Specifically, I have a js function my_int_formatter(), that I want to apply to all integers after the doc has been loaded. Best descriped by example - I want to do something like
<td>my_int_formatter({{django_variable}})</td>
I know the code above won't work, because I have to include 'script' tag, but first, I don't like the messy code, and second, javascript won't recognize python variable
I tried the following way:
HTML
<td class = 'my_integer'>{{django_variable}}</td>
JS
$(document).ready(function(){
// ....
content = $('.my_integer').html();
$('.my_integer').html(my_int_formatter(content));
...but as expected, I got wrong results because the js code applied the same html() content of the first .my_integer element in the DOM chain to all the others. Any ideas how to do this the short and correct way ?
If I understand correctly, your problem isn't with the formatting but actualy applying the formatting to each of your dom elements.
Try using jquerys .each() function and using $(this).html() to actualy grab the content.
$('.my_integer').each(function(){
content = $(this).html();
$(this).html(content+"formatted");
});
here's a quick fiddle :
https://jsfiddle.net/57rdq2a0/2/
If I understand you correctly, you want to use builtin django.contrib.humanize application: https://docs.djangoproject.com/en/1.9/ref/contrib/humanize/
You can format integers using some predefined filters, for example intcomma:
4500 becomes 4,500.
45000 becomes 45,000.
450000 becomes 450,000.
4500000 becomes 4,500,000.
Usage in your case would be like
{% load humanize %}
<td>{{django_variable|intcomma}}</td>
Also don't forget to include the app in INSTALLED_APPS
Also this question might be useful
If you want to apply filter to all variables of some kind, I suggest you to use Middleware to fiddle with response before rendering.
Yo!
I have an arbitrary javascript file, let's call it localScript, and just say it looks something like this:
<script id="myScript" type="text/javascript">
function () {
var blue = 'blue';
var person = {
firstName:"John",
lastName:"Doe",
age:50,
eyeColor:"brown"
};
var bluePerson = function () {
person[color] = blue;
};
}
</script>
I want to be able to use another externalScript to dynamically change the contents of this localScript. For this simple example, let's just say I want to update some of the values in localScript, like—maybe change age of the person object to 75. (Obviously, there's very simple ways to do this, but for my use case it's imperative that I use another externalScript to generate the contents of this localScript).
It would be easy if there was something like .innerHtml which I could use in the externalScript which would allow me to select an element and then replace the 'innerHtml' contents. The localScript, though, obviously isn't composed of elements.
As far as I know, when using a script to modify another script, there aren't any 'easy' ways to reference variables/objects/items in the script.
Things I've considered are indexOf(), search(), and match(), which I could use in externalScript to find strings inside localScript and then replace the values. I feel though as these could be performance no-no's, especially if the script grows.
Are there any easy ways to do this—with an emphasis on performance? I feel like there must be some easy way to reference one of the items in the script, though, I suppose a script is all one large string.. and maybe there is no simple way.
BTW—I'm using AngularJS, if there are any built in methods—though I think this is mostly just a javascript thing.
Thanks a bunch!
It looks like a bad idea, but... well, if it is imperative...
It makes no sense to change a script in a <script> tag - if it is in DOM, it has already executed (and no longer matters). Thus, to change the script before it has a chance to execute, you need to load it using AJAX, change the text, then eval it.
You can easily change the variables. Refer following steps
Include external script just below the script you have written.
Access the variables in the external script as if they are locally declared.
The variables you have created in above script are available in global scope and hence should be accessible from everywhere.
Note: This answer was added before the function clause was added.
I'm trying to find a way to inject a new JavaScript functions dynamically without having to do a page reload. I have a form for putting in inventory items and I would like to load or remove functions based on which manufacturer is selected. Trying to research this I thought I could accomplish this with:
document.getElementsByTagName("head")[0].appendChild
The document I was using for examples was taken from http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml. At first I was not sure if it was calling the file and so as a final test I appended the small snippet:
var petname="Spotty"
alert("Pet Name: " + petname)
and to the end of the .js file and received the alert. I was hoping to make this input page modular and dynamic. The reason I need to replace the functions is because each manufacturer uses what is called a "BOM" number which based on the values gives the unit model, condenser, electrical, etc. The scripting as it stands right now uses the onChange feature so on each drop down selection the BOM number is updated with its correct value. Conversely entering in a BOM number will select the values from the drop down menus. The JavaScript functions work as intended with the onChange's if I place it in the 'head' but when using the .appendChild none of the functions work. Should I be using something else for this?
The best approach to your problem is call a single function which accepts BOM number and manufacture type. Then fetch your unit model, condenser, electrical based upon the manufacture type
Javascript:
function GetDetails(bom, manufactureType){
switch(manufactureType){
case 'blah blah' : <do something>
break;
default:break;
}
}
HTML:
<select onchange="javascript:GetDetails(<pass selected BOM here>, <pass selected MANUFACTURER here>);"></select>
Maybe this library will help you http://yepnopejs.com
Using it you are able to load and execute JS files after some conditions.
This has been a question I've had since I started doing serious ajax stuff. Let me just give an example.
Let's say you pull a regular HTML page of a customer from the server. The url can look like this:
/myapp/customer/54
After the page is rendered, you want to provide ajax functionality that acts on this customer. In order to do this, you need to send the id "54" back to the server in each request.
Which is the best/most common way to do this? I find myself putting this in hidden form forms. I find it easy to select, but it also feels a bit fragile. What if the document changes and the script doesn't work? What if that id gets duplicated for css purposes 3 months from now, and thus breaks the page since there are 2 ids with the same name?
I could parse the url to get the value "54". Is that approach better? It would work for simple cases repeatedly. It might not work so well for complex cases where you might want to pass multiple ids, or lists of ids.
I'd just like to know a best practice - something robust that is clean, elegant and is given 2-thumbs up.
I think the best way to do this is to think like you don't have Ajax.
Let's say you have a form which is submitted using Ajax. How do you know what URL to send it to?
The src attribute. Simply have your script send the form itself. All the data is in the form already.
Let's say you have a link which loads some new data. How do you know the URL and parameters?
The href attribute. Simply have the script read the URL.
So basically you would always read the URL/data from the element being acted upon, similar to what the browser does.
Since your server-side code knows the ID's etc. when the page is being loaded, you can easily generate these URLs there. The client-side code will only need to read the attributes.
This approach has more than just one benefit:
It makes it simpler where the URLs and data is stored, because they are put exactly in the attributes that you'd normally find then in HTML.
It makes it easier to make your code work without JavaScript if you want to, because the URLs and all are already in places where the browser can understand them without JS.
If you're doing something more complex than links/forms
In a case where you need to allow more complex interactions, you can store the IDs or other relevant data in attributes. HTML5 provides the data-* attributes for this purpose - I would suggest you use these even if you're not doing HTML5:
<div data-article-id="5">...</div>
If you have a more full-featured application on the page, you could also consider simply storing your ID in JS code. When you generate the markup in the PHP end, simply include a snippet in the markup which assigns the ID to a variable or calls a function or whatever you decide is best.
Ideally your form should work without javascript, so you probably have a hidden form input or something that contains the id value already. If not, you probably should.
It's all "fragile" in the sense that a small change will affect everything, not much you can do about that, but you don't always want to put it in the user's hands by reading the url or query string, which can be easily manipulated by the user. (this is fine for urls of course, but not for everything. Same rules that apply to trusting $_GET and query strings apply here).
Personally, I like to build all AJAX on top of existing, functional code and I've never had a problem "hooking" into what is already there.
Not everything is a form though. For
example, let's say you click a "title"
and it becomes editable. You edit it,
press enter, and then it becomes
uneditable and part of the page again.
You needed to send an ID as part of
this. Also, what about moving things
around and you want those positions
updated? Here's another case where
using the form doesn't work because it
doesn't exist.
All of that is still possible, and not entirely difficult to do without javascript, so a form could work in either case, but I do indeed see what you're saying. In almost every case, there is some sort of unique id, whether it's a database id or file name, that can be used as the "id" attribute of the html that represents it. * Or the data- attribute as Jani Hartikainen has mentioned.
For instance, I have a template system that allows drag/drop of blocks of content. Every block has an id and every area that it can get dropped has one as well. I will use prefixes on the containing div id like "template-area_35" or "content-block_264". In this case, I don't bother to fallback w/o javascript, but it could be done (dropdown-> move this to area for example). In any case, it's a good use of the id attribute.
What if that id gets duplicated for
css purposes 3 months from now, and
thus breaks the page since there are 2
ids with the same name?
If that happens (which it really shouldn't), someone is doing something wrong. It would be their fault if the code failed to work, and they would be responsible. Ids are by definition supposed to be unique.
IMHO putting is at a request parameter (i. e. ?customerId=54) would be good 'cos even if you can't handle AJAX (like in some old mobile browsers, command-line browsers and so) you can still have a reference to the link.
Apparently you have an application that is aware of the entity "Customer", you should reflect this in your Javascript (or PHP, but since you're doing ajax I would put it in Javascript).
Instead of handmaking each ajax call you could wrap it into more domain aware functions:
Old scenario:
var customer_id = fetch_from_url(); // or whatever
ajax("dosomething", { "customer": customer_id }, function () {
alert("did something!");
});
ajax("dosomethingelse", { "customer": customer_id }, function () {
alert("did something else!");
});
New scenario:
var create_customer = function (customer_id) {
return {
"dosomething" : function () {
ajax("dosomething", { "customer": customer_id }, function () {
alert("did something!");
});
},
"dosomethingelse": function () {
ajax("dosomethingelse", { "customer": customer_id }, function () {
alert("did something else!");
});
}
};
}
var customer_id = fetch_from_url(); // or whatever
var customer = create_customer(customer_id);
// now you have a reference to the customer, you are no longer working with ids
// but with actual entities (or classes or objects or whathaveyou)
customer.dosomething();
customer.dosomethingelse();
To round it up. Yes, you need to send the customer id for each request but I would wrap it in Javascript in proper objects.
I'd like to start by saying that my code is working perfectly, this is more a "how best to do it" kind of question.
So I have code like this in my .aspx file:
function EditRelationship() {
var projects=<%= GetProjectsForEditRelationship() %>;
// fill in the projects list
$('#erProjectsSelect').empty();
for(var i in projects)
$('#erProjectsSelect').append('<option value='+projects[i][0]+'>'+projects[i][1]+'</option>');
var rels=<%= GetRelationshipsForEditRelationship() %>;
// etc
}
Again, it's working fine. The problem is that VS2008 kinda chokes on code like this, it's underlining the < character in the tags (with associated warnings), then refusing to provide code completion for the rest of the javascript. It's also refusing to format my document anymore, giving parsing errors. The last part is my worst annoyance.
I could put some of these in evals I guess, but it seems sorta dumb to add additional layers and runtime performance hits just to shut VS up, and it's not always an option (I can't remember off the top of my head where this wasn't an option but trust me I had a weird construct).
So my question is, how do you best write this (where best means fewest VS complaints)? Neither eval nor ajax calls fit this imo.
If your aim is to reduce VS complaints, and if you are running asp.net 4 (supporting Static client Ids), maybe a strategy like the following would be better?
Create a ASP:HiddenField control, set its ClientIdMode to "Static"
Assign the value of GetRelationshipsForEditRelationship() to this field on page load
In your javascript, read the value from the hidden field instead, I assume you know how to do this.
It's more work than your solution, and you will add some data to the postback (if you perform any) but it won't cause any VS complaints I guess :)
You could do this from your page in the code-behind
ClientScript.RegisterArrayDeclaration("projects", "1, 2, 3, 4");
or to construct something like JSON you could write it out
ClientScript.RegisterClientScriptBlock(GetType(), "JSONDeclarations", "your json stuff");
UPDATE Based on my comment
<script id="declaration" type="text/javascript">
var projects=<%= GetProjectsForEditRelationship() %>;
var rels=<%= GetRelationshipsForEditRelationship() %>;
</script>
<script type="text/javascript">
function EditRelationship() {
// fill in the projects list
$('#erProjectsSelect').empty();
for(var i in projects)
$('#erProjectsSelect').append('<option value='+projects[i][0]+'>'+projects[i][1]+'</option>');
}
</script>
I don't have VS2008 installed to test with, so take this with a grain of salt, but have you tried something like this?
var projects = (<%= GetProjectsForEditRelationship() %>);
Something like that might trick the JavaScript parser into ignoring the content of your expression.
For what it's worth, VS2010 correctly parses and highlights your original code snippet.
Is it an option to move this to VS2010? I just copied and pasted your code and the IDE interpreted it correctly.
The best solution is to put javascript in a separate file and avoid this entirely. For this particular function, you're doing server-side work. Why not build the list of options that you intend to add dynamically in codebehind, put them in a hidden div, and then just have jQuery add them from the already-rendered HTML?
If you have a situation where you really want to dynamically create a lot javascript this way, consider using ScriptManager in codebehind to set up the variables you'll need as scripts and register them, then your inline script won't need to escape
ScriptManager.RegisterClientScript("projects = " + GetProductsForEditRelationship());
(Basically, that is not the complete syntax, which is context dependent). Then refer to "projects" in your function.
(edit)
A little cleaner way to do this on a larger scale, set up everything you need like this in codebehind:
string script = "var servervars = {" +
"GetProductsForEditRelationship: " + GetProductsForEditRelationship() +
"GetRelationshipsForEditRelationship: " + GetRelationshipsForEditRelationship() +
"}"
and refer to everything like:
servervars.GetProductsForEditRelationship
If you do this a lot, of course, you can create a class to automate the construction of the script.