Dojo events: getting it to work with dynamically added DOM elements - dom

I have a method of a class as follows:
add_file: function(name, id, is_new){
// HTML: <div class="icon mime zip">name.zip <a>x</a></div>
var components = name.split('.');
var extension = components[components.length-1];
this.container.innerHTML += "<div id='"+id+"' class='icon mime "+extension+"'>"+name+" <a id='remove-"+id+"' href='#remove'>x</a></div>";
// Add event to a tag
dojo.connect(dojo.byId('remove-'+id), 'onclick', function(ev){
// here i am
});
},
All is working well, until I run this method more than once. The first time the event is registered correctly, and clicking the 'x' will run the "here i am" function. However, once I add more than one node (and yes, the ID is different), the event is registered to the last node, but removed from any previous ones.
In affect I have this:
<div id="field[photos]-filelist">
<div id="file1" class="icon mime jpg">file1.jpg <a id="remove-file1" href="#remove">x</a></div>
<div id="file2" class="icon mime jpg">file2.jpg <a id="remove-file2" href="#remove">x</a></div>
</div>
...and the remove link only works for the last node (remove-file2 in this case).

The problem is you are using the innerHTML +=
That is going to take the existing html, convert it to plain markup, and then completely create new nodes from the markup. In the process, all of the nodes with events get replaced with nodes that look exactly the same but are not connected to anything.
The correct way to do this is to use dojo.place(newNodeOrHTML, refNode, positionString)
var myNewHTML = "<div id='"+id+"' class='icon mime "+extension+"'>"+name+" <a id='remove-"+id+"' href='#remove'>x</a></div>"
//This won't work as is breaks all the connections between nodes and events
this.container.innerHTML += myNewHTML;
//This will work because it uses proper dom manipulation techniques
dojo.place(myNewHTML, this.container, 'last');

Related

how do I rendering multiple components using xstreamjs

Suppose, I want to render 3 buttons which are mapped from Array
export const homePage = soruces => {
const array$ = xs.fromArray([1,2,3])
return {
DOM: array$.map(e => {
return <button id={e}>click</button>
})
};
};
but I only get the lastest button which has id of 3
<div id="app">
<button id="3">click</button>
</div>
how do i get all the buttons rendered like this using xstream or rxjs
<div id="app">
<button id="1">click</button>
<button id="2">click</button>
<button id="3">click</button>
</div>
That depends a bit on what you want to achieve. The code you have there basically says: "I get three pieces of data, each at some arbitrary point of time" (because fromArray basically takes the data from the array one by one and puts it into the stream.
If you now what to collect the data over time, you can add fold((buttons, newButton) => buttons.concat(newButton), []).
If however you just happen to have three poeces of data, that always should be three buttons, do not use fromArray but the normal Array.map. You can then use xs.of to emit the three buttons at once (compared to one by one in the first part)

complex jquery/css selector

I need a selector for this code:
<div class="panel-heading BWHeadingForSection">
<a style="text-decoration:none" href="#faq-cat-1-sub-2" data-parent="#accordion-cat-2" data-toggle="collapse" tabindex="90">
<i class="fa fa-truck fa-lg"></i>
UPS Deutschland
<span class="pull-right">
<i class="glyphicon glyphicon-plus"></i>
</span>
</a>
</div>
And I tried that:
$('div.BWHeadingForSection a[href~="#faq-cat-1-sub-'+globalcarriernumber+'"]').on("click", function(){
count++;
if(count%2==0){
$("#faq-cat-1-sub-"+globalcarriernumber).slideToggle();
//why doesn't this selector work?
//if ( $(this+">span i").hasClass('plus') ) {
// $(this+">span i").removeClass('plus').addClass('minus');
// }
//this selector work's, at least it enters the if -block
if ( $('div.BWHeadingForSection a[href~="#faq-cat-1-sub-'+globalcarriernumber+'"]>span i').hasClass('plus') ) {
$('div.BWHeadingForSection a[href~="#faq-cat-1-sub-'+globalcarriernumber+'"]>span i').removeClass('glyphicon glyphicon-plus').addClass('glyphicon glyphicon-minus');
}
}else{
$("#faq-cat-1-sub-"+globalcarriernumber).hide();
}
});
The relevant line is "$(this+">span i")" and/or "$('div.BWHeadingForSection a[href~="#faq-cat-1-sub-'+globalcarriernumber+'"]>span i')" , which needs some adjustment. I have to replace that "plus"("") with "minus".
If I use the FireBug debugger I get for "$('div.BWHeadingForSection a[href~="#faq-cat-1-sub-'+globalcarriernumber+'"]>span i')" ,it goes into that if-block, but the symbol is still the same. And if I use "$(this+">span i")" I get(when I hove over it) : "a#faq-cat-1-sub-2",but it's not entering the if-block.
Thanks...
First of all you are mixing types here. The function $(...) accepts either an object (a DOM node or a jQuery object) or a selector string but not a concatenation of both.
So better try $(this).find('span i').
As far as I can see in your code snippet, the element you will find then, has the css classes 'glyphicon' and 'glyphicon-plus', but no class 'plus'. So you probably won't be able to remove that. Maybe you want to remove 'glyphicon-plus' and add 'glyphicon-minus'?

Issues getting validation working with dynamically generating form inputs?

I have some basic form/input html that works (including validation) if explicitly written as follows:
<form name="forms.create" novalidate>
<div class="si-container">
<div class="si-input-container">
<input class="si-input" name="someNum" placeholder="Enter a number" ng-model="formdata.number" type="number" min="40"/>
</div>
<div class="si-error">
<div ng-show="forms.create.someNum.$error.min">Error! Value must be > 40.</div>
</div>
</div>
</form>
Now what I want to do is create a directive that allows me to write the html below, but result in the html above:
<form name="forms.create" novalidate>
<div special-input name="someNum" placeholder="Enter a number" type="number" ng-model="formdata.number">
<div error-type="min" error-value="40">Error! Value must be > 40.</div>
</div>
</form>
My attempt at the special-input directive (simplified) is as follows:
.directive('specialInput', [function(){
return {
compile: function(elem, attrs){
var input = angular.element('<input class="si-input"/>');
input.attr('placeholder', attrs.placeholder);
input.attr('type', attrs.type);
input.attr('name', attrs.name);
input.attr('ng-model', attrs.ngModel);
var errorCont = angular.element('<div class="si-error"></div>');
var errors = elem.children();
angular.forEach(errors, function(error){
var err = angular.element(error);
var type = err.attr('error-type');
var value = err.attr('error-value');
input.attr(type, value);
var formName = elem.parent().attr('name');
errorCont.append('<div ng-show="' + formName + '.' + attrs.name + '.$error.' + type + '">' + err.html() + '</div>');
});
var cont = angular.element('<div class="si-container"></div>');
cont.append('<div class="si-floating-label">' + attrs.placeholder + '</div>');
cont.append('<div class="si-input-container">' + input[0].outerHTML + '</div>');
cont.append('<div class="si-underline"></div>');
cont.append(errorCont);
elem.replaceWith(cont[0].outerHTML);
}
};
}]);
Now the resultant html using the directive above looks about right. If I put {{formdata.number}} below the form the value changes as expected. The problem is that now the validation never shows.
For example, if I put the value 5 in the input and inspect the form object, I get weird results. $dirty is set to true for form, but not for form.someNum. If I put 55 in the input, $dirty is still set to false for form.someNum, but $modelValue and $viewValue both show 55.
Any ideas or suggestions? Here is a fiddle to help with any testing.
If you put 50 in the input box you should see the value below, but put 5 and the error does not appear
UPDATE
I have managed to get it working by moving the dom changes into the link function instead of the compile function, and adding this:
elem.replaceWith(cont);
$compile(cont)(scope);
I am still puzzled though, as to why this works, while altering the dom in the exact same way in the compile function doesn't work. Is anyone able to explain this?
It's because the original ng-model is still get compiled even the original DOM has already been replaced by the new one in your compile function.
The ng-model directive will register itself to a parent form in its postLink function. Due to the fact that the postLink function will be executed in reverse (child's before parent's), the new ng-model will do the registration first, thus it will be overridden by the one from the original ng-model eventually.
To avoid this problem, you could change the original ng-model to another name such as my-model, then rename it to ng-model later in your compile function.
Example jsfiddle: http://jsfiddle.net/Wr3cJ/1/
Hope this helps.

iMacros - How do I TAG URL with unique surrounding html?

I need to extract "https://www.somesite.com/Some.Name.123" from the code below.
That code segment is repeated many times, and I need the URLs ..Some.Name.X.
There are other code segments between each of the ones I'm interested in, with very different surrounding html. I don't need the ..Some.Name.x URLs in those other segments.
The following is unique to what URLs I need: "<a class="-cx-PRIVATE-uiImageBlock__image"
<div class="clearfix pvm">
<a class="-cx-PRIVATE-uiImageBlock__image -cx-PRIVATE-uiImageBlock__largeImage lfloat"
aria-hidden="true" tabindex="-1" href="https://www.somesite.com/Some.Name.123">
I don't know how to tag that preceding HTML with iMacros, or how to do that with jQuery as the structure will a bit different each time, but you could to this.
Save the web pages with iMacros. Write a program (c, etc.) to read each of the saved files and write the URLs that follow "cx-PRIVATE-uiImageBlock__image" to a file. Add that list of URLs to an iMacro, or have iMacros read the file, and then process each URL from iMacros.
You need to use some scripting.
My answer makes use of jQuery
var listoflinks = []; //array containing your links
$('a[href*="somesite.com"]').each(function () { // for each link that contains somesite.com in href
var j = $(this).attr('href'); //put the whole href in a variable
listoflinks.push(j); // put all values in an array
});
you'll end up with an array that contains all the href values you're looking for.
If you want to see an example and/or you want to play around with the script you can go here:
http://jsfiddle.net/flish/rESjg/
Edited
Your code is still not clear enough, but hopefully this may help
<a class="sibling a" href="link">sibling a</a><br />
<div class="sibling div"><br />
<a class="child a" href="start-with-link/correct-link">Child a</a><br />
</div><br />
Above is the markup I've used. What this means is that I have considered that you have the following elements:
a // with a sibking div
div // with a child a
a // and all of them have appropriate classes
For this markup you can use the following code (jQuery, of course)
var listoflinks = []; //array containing your links
$('a[class="sibling a"]').siblings('div[class="sibling div"]').children('a[class="child a"]').each(function () {
if ((($(this).attr("href")).substring(0,15))=="start-with-link"){
var i = $(this).attr("href");
listoflinks.push(i);
}
});
View detailed example at http://jsfiddle.net/flish/HMXDk/
Be that as it may, you can add more sibling and children elements in case you have other html entities you forgot to mention
<a class="-cx-PRIVATE-uiImageBlock__image" ------------------ <div class="clearfix pvm"> <a class="-cx-PRIVATE-uiImageBlock__image -cx-PRIVATE-uiImageBlock__largeImage lfloat" aria-hidden="true" tabindex="-1" href="somesite.com/some.name.123">
For example, what means ------------------ in your code above?

Unbind view model from view in knockout

I'm looking for unbind functionality in knockout. Unfortunately googling and looking through questions asked here didn't give me any useful information on the topic.
I will provide an example to illustrate what kind of functionality is required.
Lets say i have a form with several inputs.
Also i have a view model binded to this form.
For some reason as a reaction on user action i need to unbind my view model from the form, i.e. since the action is done i want all my observables to stop reacting on changes of corresponding values and vise versa - any changes done to observables shouldn't affect values of inputs.
What is the best way to achieve this?
You can use ko.cleanNode to remove the bindings. You can apply this to specific DOM elements or higher level DOM containers (eg. the entire form).
See http://jsfiddle.net/KRyXR/157/ for an example.
#Mark Robinson answer is correct.
Nevertheless, using Mark answer I did the following, which you may find useful.
// get the DOM element
var element = $('div.searchRestults')[0];
//call clean node, kind of unbind
ko.cleanNode(element);
//apply the binding again
ko.applyBindings(searchResultViewModel, element);
<html>
<head>
<script type="text/javascript" src="jquery-1.11.3.js"></script>
<script type="text/javascript" src="knockout-2.2.1.js"></script>
<script type="text/javascript" src="knockout-2.2.1.debug.js"></script>
<script type="text/javascript" src="clickHandler.js"></script>
</head>
<body>
<div class="modelBody">
<div class = 'modelData'>
<span class="nameField" data-bind="text: name"></span>
<span class="idField" data-bind="text: id"></span>
<span class="lengthField" data-bind="text: length"></span>
</div>
<button type='button' class="modelData1" data-bind="click:showModelData.bind($data, 'model1')">show Model Data1</button>
<button type='button' class="modelData2" data-bind="click:showModelData.bind($data, 'model2')">show Model Data2</button>
<button type='button' class="modelData3" data-bind="click:showModelData.bind($data, 'model3')">show Model Data3</button>
</div>
</body>
</html>
#Mark Robinson gave perfect solution, I've similar problem with single dom element and updating different view models on this single dom element.
Each view model has a click event, when click happened everytime click method of each view model is getting called which resulted in unnecessary code blocks execution during click event.
I followed #Mark Robinson approach to clean the Node before apply my actual bindings, it really worked well.
Thanks Robin.
My sample code goes like this.
function viewModel(name, id, length){
var self = this;
self.name = name;
self.id = id;
self.length = length;
}
viewModel.prototype = {
showModelData: function(data){
console.log('selected model is ' + data);
if(data=='model1'){
ko.cleanNode(button1[0]);
ko.applyBindings(viewModel1, button1[0]);
console.log(viewModel1);
}
else if(data=='model2'){
ko.cleanNode(button1[0]);
ko.applyBindings(viewModel3, button1[0]);
console.log(viewModel2);
}
else if(data=='model3'){
ko.cleanNode(button1[0]);
ko.applyBindings(viewModel3, button1[0]);
console.log(viewModel3);
}
}
}
$(document).ready(function(){
button1 = $(".modelBody");
viewModel1 = new viewModel('TextField', '111', 32);
viewModel2 = new viewModel('FloatField', '222', 64);
viewModel3 = new viewModel('LongIntField', '333', 108);
ko.applyBindings(viewModel1, button1[0]);
});