Javascript variables not updated after Angularjs ng-repeat changes made to DOM - javascript

I have an angular app which extracts data from a JSON file which populates the webpage via directive template for the user using ng-repeat.
When the page is initially loaded, the ID(from the json file) in (p) element is 1.
<p id="pollidholder" style ="display:none" ng-bind-html="item.id">{{item.id}}</p>
Once the user presses a button(Next, ng-click), the ID is updated 2 in the <p> element via a directive template. I am using the ID to register the users input.
In my back end Javascript (uservotedd.js), I am using
var pollidholder1 = document.getElementById('pollidholder').innerHTML;
to extract the ID from the <p> element.
However, the ID is not updated from 1 to 2 for the var pollidholder even though the <p> element is changed on the page itself.
How do I update my Javascript variable once the html <p> element has changed? Please help. I would like avoid major rewrite of my code. Note the Alert message is not update to reflect the change in ID when the user hits the submit button.
Please find the plunker example here https://embed.plnkr.co/3kIxhW2Iq5lKQm81csln/

Just don't use onclick in your html here:
<div class="simple-button" onlclick="idchange()" ng-click="change()" style ="font-size: 4vw ">Next</div>
Put all the code of idchange into the change function of the controller, and you will have access to all the variables you need. For example:
$scope.change = function() {
$scope.indexToShow = ($scope.indexToShow + 1) % $scope.items.length;
alert($scope.items[$scope.indexToShow].id);
}
Don't try to dodge angular's encapsulation, using onclick rather than ng-click will cause lots of confusion for beginners. Just use ng-click and you will be fine.

Related

Calling a function with ng directive from injected html via Google Chrome Extension

I'm trying to use a Chrome Extension as a complimentary tool for the web app I'm developing.
I used chrome.tab.executeScript to manipulate the DOM of the web app and append a button inside a DIV.
chrome.tabs.executeScript({
code: 'var e = document.createElement("div");' +
'e.innerHTML = "' +
'<button ng-click="myfnc()"></button>' +
'";' +
'document.getElementsByTagName("body").appendChild(e);'
});
The button is added/appended as expected.
<body>
<div>
<button ng-click="myfnc()"></button>
</div>
</body>
The button has ng-click attribute that points to function on the web app. The function is just a simple alert call.
myfnc():
alert('Hello!');
However, when I click on the button nothing happens. Do you have any idea why? Can injected html coming from Google Chrome Extension interact directly with the web page's code?
Creating an element with the attribute ng-click does not inform Angular that the element exists within the page, as parsing of the document occurs once during bootstrap. You therefore have an element which exists 'outside' of any Angular scope, meaning the attribute ng-click just exists and has not caused the corresponding directive (ngClick) to be invoked.
Providing of course that the page has Angular, you will want to use the $compile service along with the desired $scope to compile the element after it has been inserted into the page.
A comprehensive solution is outside of the scope of your question. (Why does nothing happen when the button is clicked?) If you want to read more about using injected code with Angular, read up on:
the angular.element#scope() method, which lets you pick up the $scope object of an element,
appending an angular.element using the append() method available on angular elements
using the derived $scope object in compiling elements such that Angular is aware of it
The steps you want to take are something like...
Get the $scope you want to insert the element within:
var angular = document.querySelector('body');
var scope = angular.element(angular).scope();
Insert the new element into the page:
var newElem = angular.element('div');
angular.append(newElem);
$compile the element with this $scope:
$compile(newElem)(scope);
More information on dynamically creating Angular elements can be found in the answer to this question.

angularjs drag and drop html content

I am trying to create a live HTML editor with AngularJS, where user can drag and drop some DOM elements to a container and it will render the final HTML page, for example, there is a button (or whatever) that says "Drag me to create an input", then user will drag that item into the container and an input field will be rendered...
I've been trying ngDraggable module but isn't what I needed...
I think a way is to hold the HTML code into some $scope variable, and when the user drops the button into the container, $scope.input which contains <input type="text" placeholder="Something"/> will be rendered inside container...
My final step is to enable inline edit to thoose HTML elements generated, in order to let the user create some content agains that final template.
When the user finish, the whole HTML code will be saved somewhere or downloaded...
I don't need something complex, just a few predefined elements that can be dropped...
I hope I've explained it correctly.
There is some example found at http://nboychev.com/tests/angular/Drag%20Drop%20iFrame%20using%20Angular%20JS%20Directive.html that does exactly what I want (see source), BUT:
It's using jQueryUI (Which I don't want to use, but if its needed then I will)
It isn't working on my project. I must say that I'm using RequireJS, but everything works on my angular app but the directives mentioned on the link above. Draggable is not working, I do not have any errors and I've checked that the code is being executed, doing some console.log() stuff, but still not working...
But it contains what I want, draggable stuff with HTML injected that renders content on an iframe (or a div, or whatever) and that new content is also droppable, to insert more stuff inside that code. Give it a try, and guide me a little. Thanks
I think it's better if you try using something as jui
On the other hand you can try implementing your own directive:
<br>
<h4>Drop Area</h4>
<div droppable jui-options="{addClasses: false}" class="drop-area"></div>
<div class="read-out">
<span class="text-info"><strong>Draggable ID</strong></span>: {{obj.id}}<br><br>
<span class="text-info"><strong>Content</strong></span>: <span ng-bind-html="obj.content"></span><br><br>
<span class="text-info"><strong>Actual Content</strong></span>: {{obj.content}}<br><br>
</div>
</div>
http://codepen.io/m-e-conroy/pen/jCdhu
Sounds like a fun project.
This is a simple link function using JqueryUI and Angular. You'll need an HTML element in scope with class "draggable". Use destroy() when finished.
Adding HTML to the DOM from the scope, check out this solution by Josh David Miller: http://jsfiddle.net/paulocoelho/fBjbP/2/
Or for simple HTML elements for visual display only you could try using ngBindHtml, ngRepeat and ngSanitize. Then adding / removing HTML elements to and from an array should display them in a container.
link: function($scope, $elem, $attr){
$elem.find(".draggable").draggable({
start: function(event, ui){
$(ui.item).addClass("shadow");
},
stop: function(event, ui){
$(ui.item).removeClass("shadow");
// DO SOME ANGULAR STUFF HERE
}
});
}

angularjs directive: not replace nor transclude

I need to append a directive's template AFTER an input field. The original input field needs to remain - I can't just create a duplicate of it. My thought for this was to, in the controller, use jQuery to add a DIV after the input field, and add an attribute for the directive to the div. However, in practice, that doesn't work - the div is created and added, but the directive doesn't activate.
The problem, I know, is that the jQuery-added div is not yet recognized by the angularjs controller - it appears AFTER angularjs runs over the controlled html.
I know that part of the problem is that you're not supposed to use jQuery in the controller, but I honestly can't think of another way to do it. Is there some way to cause the angularjs controller to look at this new div?
The original HTML looks like the following.
<input name="generatedString_1234567890">
I run jquery over the page to add a controller to the body. The relevant code looks similar to this:
jQuery('body').attr('ng-controller','MainCtrl');
angular.module('app',['DataTools']);
angular.element(document).ready(function(){
angular.bootstrap(document, ['app']);
});
Inside the angularjs, I run a resource to get a json string containing the relevant changes to the DOM, in terms of attributes that need to get added to the inputs and certain understood flags that require specific coding. The relevant task I'm trying to accomplish looks like this, where $(this) is the input field for the DOM.
jQuery(document).find('input.FormField,select.FormField').each(function(){
// Inside a case statement based on certain flags
var d = $('<div/>');
d.attr("ng-model", 'adors.'+label);
// Relevant code that adds attributes to the div - including the required directives
$(this).hide().after(d);
}
I am trying to create code that looks more like this (VERY GENERIC):
<input name="generatedString_1234567890" ng-hide="true" ng-model="input.uniqueKey">
<div special-input="time" ng-model="input.uniqueKey">
<select ng-repeat="hour in hours">
<select ng-repeat="minute in minutes">
</div>

How to use ng-bind-html with two-way databinding?

I'm currently working on a simple editor built with angular. The main textbox is just a div with the contenteditable set to true and the ng-bind-html attribute, like this:
<div contenteditable="true" ng-bind-html="field.content">HTML content here</div>
The value is set and rendered properly with the right tags and look at page load. But since it's only bound one way I my data or model isn't updated upon edit.
I've tried some contenteditable directivs but most of them requires ng-model, but if I add that my html tags are not rendered and converted into symbols.
How would I go about updating my data after the div's content have been changed and also keep the HTML tags and so on formatted correctly?
EDIT: Just using this temporary "fix" for the time being, but I would like something more robust.
<div contenteditable="true" ng-bind-html="field.content"
ng-blur="saveHTML($event)"></div>
$scope.saveHTML = function(event) {
this.field.content = event.target.innerHTML;
}

Accessing text field within iframe from parent document and store for use

I have two documents - main.html (parent document) and enterKey.html (child document).
Within main.html
<script>
...
</script>
<iframe id="key" src="enterKey.html"></iframe>
Within enterKey.html:
<input id="sbox" type="text">
I am trying to access the value entered in sbox for use in main.html. But because the user will be continuously updating sbox I need for the updates to be reflected in main.html.
For example if the user types into sbox "hello worlx" then corrects it a second later to "hello world" then the change must be reflected immediately in the parent document.
How can this be achieved?
I've been looking at using document.getElementById('key').contentWindow.sbox but don't know how to use it in the context of my example.
Many thanks in advance!
I think the best approach would be to set the onchange function on the field and they you can access elements on the parent page with:
parent.document.getElementById("id")
Example:
In the enterKey.html you would set the onchange for your text field by:
<input id="sbox" type="text" onChange="enterKeyFunc()">
Then also in enterKey.html you would have a function called enterKeyFunc() or something that would look kinda like this:
function enterKeyFunc()
{
parent.document.getElementById("mainHTMLFieldId").value = document.getElementById("sbox").value;
}
Then in main.html make sure that the field you wanted to keep synced has an id of "mainHTMLFieldID" and that should do it. Let me know if you have any other questions.
Edit again:
I think if you want to set a variable in the parent change the above js from
parent.document.getElementById("mainHTMLFieldId").value = document.getElementById("sbox").value;
to
parent.globalVariableName = document.getElementById("sbox").value;
This of course would only work if the variable you are trying to set is a global variable on the parent page. I haven't done this for a long time personally but I believe this can be done.
It's a bit more simple than you think:
var iframe = document.getElementById('key').contentWindow;
var value = iframe.getElementById('sbox').value;
Just treat the iframe as a new document element.

Categories