Angular and jQuery: Events not attached to dynamically created HTML elements - javascript

I am using jQuery to dynamically create new elements in an Angular Form. The form is built using Template Driven Forms approach. The dynamic elements are successfully created but they are not assigned events/callbacks apparently because the component was already compiled and did not re-compile for the dynamic elements. This keeps the new elements from reporting data or responding despite that the name attribute and ngModel directive is assigned to it. How do I go about this? I have to read the form data for storing in database.
The TypeScript File code which generates the new component's HTML is as under (do not focus on class/ID names etc for I modified them for simplifying question statement). The function uses a counter for assigning unique name to the new input element.
private counter = 0;
increaseElementDynamically(){
this.counter++;
var htmlTagDef = '<input #benchReff'+this.counter.toString()+' ="ngModel" type="text" class=" form-control mb-3" id="bench'+this.counter.toString()+'" required name="bench'+this.counter.toString()+'" ngModel>';
$("#myDiv").append(htmlTagDef);
console.log(htmlTagDef);
}
The input element is written as under in the component's HTML file
<div class="form-group col-lg-3 border-right border-primary">
<label for="benchGroup">Bench Members</label>
<div ngModelGroup="benchGroup">
<div id="customDiv">
<input #benchReff ="ngModel" type="text" class=" form-control mb-3" id="bench" required name="bench" ngModel>
</div>
</div>
<button type="button" class="btn btn-primary float-right m-3" (click)="iincreaseElementDynammically()">Add</button>
</div>
Even if a simple click event is assigned, it wont work for the dynamically created elements since they were created on runtime.
How can the proper dynamic behavior be achieved with functionality?

you need to attach click event or any other event handler using parent element delegation.
$('#myDiv').on('click','#bench', function(){ // will be triggered on click of bench input which is added dynamically
console.log('clicked');
});

Solution: jQuery was operating on front end whereas the component was recompiling. Better approach was to eliminate jQuery and use ngFor directive. For new [dynamic] elements, new IDs can be pushed to an array with ngFor operating on same array to dynamically create elements which fully supported Angular functionality.

Related

How clone and init Datepicker via vanilla JS correctly in Tailwind Elements?

I use Tailwind Elements library with Tailwind CSS in my project.
Everything works perfectly, but there is one problem appeared... because I'm trying to clone the element and add to the page a copy.
I do the clone of a form with cloneNode method like this:
I have hidden HTML template:
<template id="temp">
<form>
<div class="datepicker relative mb-3" data-mdb-toggle-button="false">
<input type="text" placeholder="Select a date" data-mdb-toggle="datepicker" />
<label class="text-gray-700">Select a date</label>
</div>
</form>
</template>
When user clicks a button somewhere in the page "+ Add new form", then the new form will be added to the page. There can be many forms as user wants. So I want to use this template to clone it and insert new and new and new nodes to the page. JS:
import 'tw-elements'
let newForm = document.querySelector("#temp").content.cloneNode(true); // clone the <form>
newForm.appendChild(document.body); // add to the <body>
After these manipulations the new form added to the page, but the Datepicker does not work.
As I understood, I should call the initialization of the element programmably via JS like this:
let datePicker = newForm.querySelect('.datepicker');
datePicker.tweDatePicker(); // smth like that maybe
Right? But how? I cannot find in the docs how to call and init the Datepicker (or any other component) via vanilla JS programmably?
Maybe you know the better approaches.

JQuery: dynamically update HTML so that it is recognized by JQuery

So, first of all, I am a newbie in Web (html/js)
What I want to achieve:
I have a custom tree and I want to be able to dynamically (using jquery) create children for that tree:
Html:
<ul>
<li>
Item
<input type="button" value="+" class="childAdder">
<ul class="childrenList"></ul>
</li>
</ul>
JS:
$(".childAdder").click(function() { // add child which is the same as root
$(this).parent().children(".childrenList").append(
'<li>Item</li>\
<input type="button" value="+" class="childAdder">\
<ul class="childrenList"></ul>'
);
});
And as you can see, I know how to add a child (more or less, an advice is always welcomed). The problem is, however, that this code only works for items that are "predefined" in html --> everytime I dynamically(via JS) add a child, this code just does not execute for this newly created element (tried to do alert('Hello'); - nothing is seen)
Question:
I assume I need to somehow "properly" add my new child to the DOM(?) of HTML or whatever, so that it is then recognized by JS, right? (but that seems to be only achieved trough HTML static page, no?)
Anyway, how do I make this thing work: add new child so that the same JS code that is executed for HTML element is executed for the element created by JS
is there a solution or the whole implementation is just wrong?
You need to use event delegation for this to work. This is done by using the on JQuery method for event wiring and modifying the parameters.
Also (FYI), a <ul> element can only contain <li> elements as children. Your bullet/nested list structure is invalid.
Lastly, in the HTML string you were appending included random \ characters, which are not needed and are actually invalid at those locations.
Here's the correct implementation with the HTML corrected to be valid.
// JQuery event delegation requires the use of the "on" method and then you
// bind the event to an object that is sure to always recieve the event (document
// works well here because of event bubbling). Then you specify the target element
// that the actual event will be triggered from.
$(document).on("click", ".childAdder" ,function() {
// add child which is the same as root
$(this).parent().children(".childrenList").append("<li>Item<br><input type='button' value='+' class='childAdder'><ul class='childrenList'></ul></li>");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<!-- <ul> and <ol> elements can only have <li> elements as thier children (aside from
comments like this one). To properly nest lists, organize the HTML as follows: -->
<li>Item<br>
<input type="button" value="+" class="childAdder">
<ul class="childrenList"></ul>
</li>
</ul>

How do we set mask on a dynamically inserted input?

Since Angular-UI-Mask is acting oddly, I'm using jquery-inputmask to some of my inputs, but when an input is dynamically inserted ny Angular it gets no mask:
<li ng-repeat="item in items">
<input type="text" name="birth_date" class="span2 format_date" ng-model="birth_date" placeholder="Data de Nascimento" required />
</li>
This is the related script
<script type="text/javascript">
jQuery(document).ready(function(){
$(".format_date").inputmask("99/99/9999");
});
</script>
Is there anything I can do to force it to set the mask to new inputs?
jQuery plugins like jQuery.inputMask work by (as your code shows) attaching behaviour to DOM elements when the document is 'ready'. This will run once, and never again, so for dynamically-added content this approach doesn't work.
Instead, you need something that will run whenever the corresponding DOM is changed. So whenever an 'item' in your 'items' list is added, the element is added and the corresponding jQuery function is run against that element. You need to use AngularJS for this and you could write your own directive, but thankfully, someone has already written the code for you: the jQuery Passthrough plugin as part of Angular UI's UI.Utils.
Here is a working Plunkr.
You need to include the script at the top, like so (I downloaded it from GitHub):
<script src="ui-utils.jq.js"></script>
Load the module into AngularJS, for example:
var app = angular.module('myApp', ['ui.jq']);
And then use the directive in your HTML markup:
<input type="text" ui-jq="inputmask" ui-options="'99/99/9999', { 'placeholder': 'dd/mm/yyyy' }" />

getElementById returns allways null if i search for new created Element

i want to duplicate one element and then assign to new id and names. my aim is later i want find that element again. here is an example:
<div id="contDiv">
<div style="float:left"> filename </div>
<div style="margin-left: 323px"> playtime(in Sek.) </div>
<div id="tl_Einst" class="editor-field">
<select id="Medianames" onchange="change(this)" name="TL_Medianame">
<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg- for="Filename"> </span>
<select id="playtime" class="playtime" name="playtime" disabled="">
</div>
<input id="More" type="button" onclick="Addfiles()" value="+">
</div>
in the runtime i duplicate the Element "tl_Einst" with its children. then the user changes the content of the combox then i read them and send them to the server. later i get an answer. so i call the GetelementById("newElementID"), but i get always null, if search for the new created element. what can i do?
thank you
marek
The proper syntax is:
var el = document.getElementById("newElementID");
Typically, # is for JQuery or other javascript frameworks, or as #Quention mentions below, can also be used with document.querySelector.
Where are you adding the element to the DOM? Could you include that code too?
Have you added it to the page?
getElementByID will only find an element in the DOM. You can either add it to the page or simply assign it to a variable.
var newElementID= document.createElement("p");
You can now find it in your code with newElementID even if it's not in the page.

Why are edit in place forms rendered together with the display version instead of being rendered on the fly?

Is there a specific reason that most everyone implements edit-in-place as a shown 'display' div and a hidden 'edit' div that are toggled on and off when somebody clicks on the associated 'edit' button like so?
<div id="title">
<div class="display">
<h1>
My Title
</h1>
</div>
<div class="edit">
<input type="text" value="My Title" />
<span class="save_edit_button"></span>
Cancel
</div>
</div>
Everywhere I look, I see edit-in-place basically handled like this. This approach certainly makes sense when you are rendering all views on the server side and delivering them to the client. However, with pure AJAX apps and frameworks like backbone.js, it seems that we could make our code much more DRY by rendering edit-in-place form elements on the fly as necessary, possibly even making a factory method that determines which form element to render. e.g.
an H1 element with class "title" is replaced by <input type="text" />
a span with class "year_founded" is replaced by <input type="number" min="1900" max="2050" />
a span with class "price" is replaced by an input with the appropriate mask to only allow prices to be input.
Is this practice of rendering all edit-in-place form elements a historical legacy leftover from when pages were rendered on the server-side?
Given the flexibility and power we have with client-side MVC frameworks like Backbone.js, is there a reason for not creating and inserting the form elements on the fly when necessary using a factory method? Something like this:
HTML
<div id="description">
Lorem ipsum dolar set amit...
</div>
<span class="edit_button"></span>
Backbone.js View
events: {
"click .edit_button": "renderEditInPlaceForm",
},
renderEditInPlaceForm: function:(e) {
var el = $(e.currentTarget).previous();
var id = el.attr('id');
var value = el.text();
var tagName = el.tagName();
var view = new editInPlaceForm({
id: id,
type: tagName,
value: value
});
$("#id").html(view.render().el)
},
Where editInPlaceForm is a factory that returns the appropriate edit-in-place form element type based on tagName. This factory view also controls all its own logic for saving an edit, canceling an edit, making requests to the server and rerendering the appropriate original element that was replaced with the .html() function?
It seems to me that if we use this approach then we could also render the <span class="edit_button"></span> buttons on the fly based on a user's editing rights like so:
<h1 id="title">
<%= document.get("title") %>
</h1>
<% if (user.allowedToEdit( document, title )) { %>
<span class="edit_glyph"></span>
<% } %>
where the allowedToEdit function on the user model accepts a model and attribute as its arguments.
It's an interesting idea. The devil is in the detail.
While your simple example is easily rendered as an editable form on the fly, things quickly get trickier when dealing with other data types.
For example - suppose my edit form requires the user to choose a value from a select list. On the display form I can simply display the user's choice, but for the edit form I am going to need those other available choices. Where do I hide them on the display? Similar issues exist for checkboxes, radio lists...
So, perhaps we should consider rendering the edit form, and then deriving our display-view from that?
After 5 Backbone apps I came to same thoughts.
When things are complicated you have forms to show relations between user data,
but in simple cases you just need input, select, checkbox over h1, div or span
Now I am searching for jQuery plugin to make simple in place editing without ajax.
jQuery but not Backbone becuase I don't want to be tight coupled with Backbone for such small thing.
Likely to wright my own jQuery + Synapse plugin http://bruth.github.com/synapse/docs/.
Synapse for binding with model and jQuery for input placing

Categories