I know two ways to bind HTML to client-side JavaScript code and stay a kind of object-oriented:
Use a lot of IDs (or special CSS class names, or some other distinct HTML attributes) in HTML and do a "harvest" in JS initialization method (or request each DOM object each time, right before use);
Do not write HTML at all. Construct an element at runtime, in initializer, and remember a reference to DOM object (or jQuery object) in a variable.
Are there some other ways that allow to use design-time phase (writhing HTML) which is much more convenient than doing all the work at runtime, and at the same time do not use a lot of identifiers of any kind having to maintain their uniqueness?
AngularJS is the framework you want to use for 2-way data binding.
I used AngularJS for multiple projects now, combined with nodeJS, and I never looked back at jQuery, you keep your code clean with the MVC pattern and manipulating the DOM is made easy and clear.
Example for 2 way data binding:
HTML
<p>{{elementText}}</p>
<input type="text" ng-model="item.value" />
JavaScript/Controller
$scope.item = {
value: ''
};
$scope.elementText = "The text you want to display";
console.log($scope.item.value); //Directly get your values from the scope.
Want to assign values to <select> boxes or fill <table>'s using JSON data? No problem, AngularJS got you covered.
Interesting AngularJS features:
ng-model
ng-repeat
Animations
Custom directives
I hope this will help you!
Related
I'm new in AngularJS, using it for two months in a project. I've learned how to use directives and theirs scopes (false, true, obj literal), but there's some questions about it...
First of all, we have some ng-repeats and directives with some behaviors, I tried to present a equivalent scenario in this link.
I didn't figure out how to access a function (testfn - within ng-controller) inside a directive child of another directive myItemDirective. But in myStepDirective it's accessible, I tried to pass it like in the first "layer" but didn't work.
PS.1: I created a myStepDirective with a isolated scope for other examples, if you need, just uncomment to test. Both I got a way to access params/functions from parent (controller), but not inside a grandchild.
Why directive's scope params doesn't work with camel case params? I don't remember to read some hint in AngularJS docs... typewithnocase inside myItemDirective works but typeList not.
Thanks!
EDITED]
For your 1. Here is a working fiddle working with limited scope and camel to snake case conversion : https://jsfiddle.net/wu0avqau/
I spend a loooooong time not understanding why it didn't worked but you juste forgot a = in your ng click inside your second directive
ng-click"testfn()"
For your 2. I can refer you to the documentation : https://docs.angularjs.org/guide/directive
Normalization
Angular normalizes an element's tag and attribute name to determine which >elements match which directives. We typically refer to directives by their case->sensitive camelCase normalized name (e.g. ngModel). However, since HTML is case->insensitive, we refer to directives in the DOM by lower-case forms, typically >using dash-delimited attributes on DOM elements (e.g. ng-model).
The normalization process is as follows:
Strip x- and data- from the front of the element/attributes.
Convert the :, -, or _-delimited name to camelCase."
basicaly your myItemDirective would be my-item-directive inside your template but still be myItemDirective inside your js.
Good luck,
Thibaud Lamarche
I'm using Knockout.js for a number of things in a project.
Among other things it's used to build XML config for a third-party tool on the fly, which requires a very specific, strict markup.
This means, that all resulting markup needs to be without any data-bind attributes.
Currently I can achieve this by additionally binding
<Element data-bind="attr: {'data-bind': false}" />
or alternatively doing a separate, additional loop over the whole resulting markup, removing all data-bind attributes.
Neither of the solutions are too straightforward, the first one meaning very verbose templating, the second requires an additional pass over the whole result.
Does Knockout offer a better alternative to remove all data-bind attributes after bindings have been applied?
Maybe preprocessNode can help:
You can hook into Knockout’s logic for traversing the DOM by providing a node preprocessor. This is a function that Knockout will call once for each DOM node that it walks over, both when the UI is first bound, and later when any new DOM subtrees are injected (e.g., via a foreach binding).
The following will remove the data-bind attribute:
ko.bindingProvider.instance.preprocessNode = function(node) {
if (node.removeAttribute) {
setTimeout(function() {
node.removeAttribute('data-bind');
}, 0);
}
};
See http://jsfiddle.net/jfjbpbtq/
setTimeout is needed because knockout will read the data-bind attribute after calling preprocessNode.
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.
I'm trying to create a "feed" using data received from another site. It comes as an array of similar objects. I've tried a couple of ways of displaying the data but keep changing my mind on the best way to do it. Currently I'm using jQuery loop through the objects and create the same divs for each one by nesting $(<div>).html()
This uses much less code than my previous method of plain javascript to add new elements but I'm not sure if this is the best use of jQuery. Would there be a better way of doing this?
Possibly creating a new JavaScript object for each array element?
EDIT: here's a small snippet of code. It's not closed off and I've changed the class names and variables but it's a rough idea.
$.each(feed.data, function(i, var_name) {
$("<div>").attr({
"id" : var_name.created_time,
"class" : "Post"
}).html(
$("<img>").attr({
"class" : "Picture",
"src" : "some_src" + var_name.id + "/picture"
});
Your method is the least efficient of all the possible methods due to the significant number of function calls made for each individual element.
Although you could add a template engine as suggested, it's worth learning how to create html strings as well, and will find that wrapping each element in $() doesn't really reduce code
var newHtml=[];
$.each(feed.data, function(i, var_name) {
newHtml.push('<div id="'+var_name.created_time+'" class="Post">');
newHtml.push('<img class="Picture" src="some_src"' + var_name.id + '/picture" />');
/* some nested data*/
$.each( var_name.someRepeatingElements), function(i,val){
newHtml.push('<p>'+val.content+'</p>')
});
newHtml.push('</div>')
});
/* after looping all data only make one append to DOM*/
$('#someDiv').append( newHtml.join(''))
Semantically this is easier to read than nesting numerous $.html() calls also
In many cases without a lot of complicated nesting, creating your own strings is less work than creating a template and code to populate template
I've been using KendoUI and have been using they're command functions. However to call JS I must call named jS functions. No huge deal. When I use the "This" key word it brings back the entire grid and I mus find a value of a child from a sibling of the same parent elements and i wound up doing this ugly thing. The question I have is how can I turn this "thing" into something jqueryable readable and comprehensible
function AddRole(e) {
var $ParentNode = e.target.parentNode.parentNode.children[1].children[0].getAttribute("value", 0);
}
Sorry, but you have other problems.
If you rely on such a structure e.target.parentNode.parentNode.children[1].children[0], your Markup and JS do not scale at all.
Use the oppurtunity to create scalable and consistent code. Or at least, set some id, class or html5 data attribute on the children[0] element in order to identify it properly.