querySelectorAll in AngularJS partial ng-view - javascript

I'm trying to use queryselectorAll to get all elements with a certain class name. I have a problem where I can only get results from elements in the index.html page. If I try to get a nodelist from a partial (ng-view) html page I get an empty result back.
App.js
var myApp = angular.module('myApp', ['ngRoute', 'articleControllers']);
myApp.config(['$routeProvider', function($routeProvider){
$routeProvider.
when('/main', {
templateUrl: 'lib/partials/main.html',
controller: 'MainController'
})
}]);
controller.js
var articleControllers = angular.module('articleControllers', []);
articleControllers.controller('MainController', ['$scope', '$http', function ($scope, $http){
$http.get('http://...').success(function(data) {
$scope.articles = JSON.parse(data);
});
}]);
index.html
(body, header, ...)
<section ng-view>
</section>
(footer, ...)
lib/partials/main.html
...
<div class="nextArticle">
<button class="next_btn" title="View next article"> link text</button>
</div>
...
finally: helper.js (which is a script I call like every other script in index.html)
var elList = document.querySelectorAll('.next_btn');
Array.prototype.forEach.call(elList, function(el) {
console.log("Found: " + el);
});
So to recap:
Its like querySelectorAll can only find elements in the index.html and not in the partial views with ng-view.
Anyone an idea of what could be wrong?
I'm trying not to use jquery.

Move your helper.js code to custom directive. https://docs.angularjs.org/guide/directive
What are Directives?
At a high level, directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS's HTML compiler ($compile) to attach a specified behavior to that DOM element or even transform the DOM element and its children.

That is because ng-view load the template after angular gets loaded, and the JS code which you have added is firing before the ng-view renders the template, I think you could solve this by writing a directive for ng-view that will will fire your jQuery code once your ng-view content gets loaded
Basically you need to wrap your code in element.on('load' event so that it will ensure that code will available when jQuery code is firing
app.directive('ngView', ['$timeout', function($timeout){
return {
restrict: 'AE',
link: function(element, attrs){
$timeout(function(){
element.on('load', function(event){
//your jQuery code will lie here
//that will also get the DOM of ng-view template
});
});
}
}
}]);

Related

angularjs click event, after appending content [duplicate]

I am working with angularjs 1.2.0-rc.3. I'd like to include html code into a template dynamically. For that I use in the controller :
html = "<div>hello</div>";
$scope.unicTabContent = $sce.trustAsHtml(html);
In the template I got :
<div id="unicTab" ng-bind-html="unicTabContent"></div>
It works fine for regular html code. But when I try to put angular template it is not interpreted, it is just included in the page. For example I'd like to include :
<div ng-controller="formCtrl">
<div ng-repeat="item in content" ng-init="init()">
</div>
</div>
Thanks a lot
This solution doesn't use hardcoded templates, and you can compile Angular expressions embedded within an API response.
Step 1.
Install this directive: https://github.com/incuna/angular-bind-html-compile
Step 2. Include the directive in the module.
angular.module("app", ["angular-bind-html-compile"])
Step 3. Use the directive in the template:
<div bind-html-compile="letterTemplate.content"></div>
Result:
Controller Object
$scope.letter = { user: { name: "John"}}
JSON Response
{ "letterTemplate":[
{ content: "<span>Dear {{letter.user.name}},</span>" }
]}
HTML Output =
<div bind-html-compile="letterTemplate.content">
<span>Dear John,</span>
</div>
For reference sake, here's the relevant directive:
(function () {
'use strict';
var module = angular.module('angular-bind-html-compile', []);
module.directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}]);
}());
This is what I've made, no idea if it's the angular wayTM, but it works and is super simple;
.directive('dynamic', function($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, element, attrs) {
scope.$watch(attrs.dynamic, function(html) {
$compile(element.html(html).contents())(scope);
});
}
};
});
So;
<div id="unicTab" dynamic="unicTabContent"></div>
Edit: I found the angular way, and it's super simple.
$templateCache.put('unicTabContent', $sce.trustAsHtml(html));
<div id="unicTab" ng-include="'unicTabContent'"></div>
Don't need to make your own directives or anything.
But it's a bind-once sort of deal, it wont see changes made to your html like the custom directive does.
As Vinod Louis says in his comment, the best way to do that was to use templates. I had to define a template outside of the regular code, for example I added that code inside of my index.html :
<script type="text/ng-template" id="unic_tab_template.html">
<div ng-switch on="page">
<div ng-switch-when="home"><p>{{home}}</p></div>
<div ng-switch-when="form">
<div ng-controller="formCtrl">
<div ng-repeat="item in content">{{item.name}}:{{item.value}}</div>
</div>
</div>
<div ng-switch-default>an error accured</div>
</div>
</script>
This template is conditional, so depending on the value of $scope.page, it switches between the 3 templates (the third being an error handler). To use it I had :
<div id="unicTab" ng-controller="unicTabCtrl">
<div ng-include="'unic_tab_template.html'"></div>
</div>
That way my page changes depending on the $scope inside of my unicTabCtrl controller.
To conclude the idea of inserting angularsjs template seams to be difficult to realize ($compile seams to be the solution, but I wasn't able to make it work). But instead you may use conditional templating.
I was trying to do the same thing and came across this module.
http://ngmodules.org/modules/ng-html-compile
I just included it and then I was able to use "ng-html-compile" instead of "ng-bind-html"
My solution to similar problem in my current app without using template (not elegant, but working):
directive('ngBindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
compile: function compile(tElement, tAttributes, transcludeFn) {
return function postLink(scope, element, attributes) {
scope.$watch(function() {
return scope.$eval(attributes.ngBindHtml);
}, function(newValue, oldValue) {
$compile(element.children())(scope);
});
};
}
};
}]);
It requires ngBindHtml on the same element and compiles the element content after it changes with ngBindHtml.
<div id="unicTab" ng-bind-html="unicTabContent" ng-bind-html-compile></div>
ng-html-compile looks similar but at first glance it won't be recalculated when the template content is changing. But I haven't tried it.
One way is use a directive for purpose of inserting custom templates that include angular expresssions
<div id="unicTab" unic-tab-content></div>
app.directive("unicTabContent",function(){
return {
restrict:"A",
template:'{{unicTabContent}}'
}
})
The code below is much simpler using Angular's built-in $interpolate and $sce objects. First inject the $interpolate and $sce Angular objects into your directive as you do anything custom you need in your directive.
amqApp.directive('myDir', ['$interpolate', '$sce', function ($interpolate,$sce ) {...}
Then create all your scoped variables found in your imported html expressions...
$scope.custom = 'Hello World';
Next use $interpolate to process your custom HTML and its expressions...then make sure you use the $sce object to trust it as HTML before binding...
var html = $interpolate('<b>{{custom}}</b>')($scope);
$scope.data = $sce.trustAsHtml(html);
Finally, in your view, just make sure use an element with the "ng-bind" or "ng-bind-html" on it in your view display. I found the $sce piece wont display the HTML as HTML (sees it as text) if you don't bind it in your html template like this...
<span ng-bind-html="data"></span>
You should see in bold...
Hello World
I used this trick to import in text/HTML with custom angular {{expressions}} from a web.config.
A lot simplified solution of #clement-roblot based on built-in templates.
Controller:
app.controller('TestCtrl', [
'$scope',
'$templateCache',
function ($scope, $templateCache) {
$templateCache.put('test.html', '2 + 2 = {{ 2 + 2 }}');
}
]);
View:
<div ng-include="'test.html'"></div>

AngularJS - Dynamic HTML that contains the controller code

I have a AngularJS page that displays a popup. The HTML for the popup is dynamically retrieved from the server using an AJAX call. This dynamic HTML contains a new controller and the necessary AngularJS code for the controller as well.
This code works only if the child page JavaScript code is present in the parent page.
I would like to keep all the child page code in the child page itself.
Can someone please point out what I am doing wrong?
Main Page
<div id="mainPage" ng-controller="mainController">
Contains a table that displays a bunch of rows.
</div>
<div id="popup">
Dynamic HTML retrieved from AJAX goes here.
</div>
<script type="text/javascript">
angularMainApp.controller('mainController', ['$scope', '$http', '$compile', function ($scope, $http, $compile)
{
$scope.activateView = function(ele)
{
$compile(ele.contents())($scope);
$scope.$apply();
};
$scope.buttonClick = function()
{
$("#popup").html( dynamicHTMLThroughAJAX );
$scope.activateView($("#divCreateTemplatePopup"));
return;
}
return;
}]);
</script>
Dynamic HTML Content
<div ng-controller='childController'>
Some HTML here.
</div>
<script type="text/javascript">
angularMainApp.controller('childController', ['$scope', '$http', function ($scope, $http)
{
}]);
</script>
Angular need to know that you changed the content and realize that it should update it self too .
so , what you have to do is tel him that apply the watchers and other stuff to the current DOM .
for doing that you shall use $scope.$apply(); . by calling this function you force angular to add watchers and other stuff's to the DOM and now it can work with you'r new contents too .
i suggest you to read about digest in angularjs and see how it works .
here's some link about your problem to see the problem better :
AngularJS and scope.$apply
Digest cycle and $scope
AngularJs docs : $scope.$apply()
When to use $scope.$apply()
AngularJS: $watch, $digest and $apply
good luck and have fun .

Jquery append() not working with angularjs

We are working with jquery 1.9.1 and angular 1.2.13. We are using a wysiwyg editor that works great, we save the html into the database and load the html back using jquery append function and works fine. Now we are trying to append the same html into a div tag (the wysiwyg editor also uses a div) and the append function it's not working. We check in the console, and the string we are trying to append is there, also jquery grabs the element (also checked in the console log) but the append function it's not working.
PD: I apologize for my english
The html
<div data-ng-controller="PreviewCtrl">
<div class="container">
<div id="resumenPreview"></div>
</div>
</div>
The controller
angular.module('module').controller('PreviewCtrl', ['$scope', '$routeParams', '$location', '$http', 'selectedElement',
function ($scope, $routeParams, $location, $http, selectedElement) {
$scope.id = $routeParams.id;
$scope.mensaje = $scope.id;
$scope.imagen = null;
$scope.dataImagen = null;
//is not working either
$('#resumenPreview').append("hola");
$scope.pageLoad = function () {
var x = selectedElement.data.Resumen;
//This is properly displayed in the console
console.log(x);
//This too, is displayed in the console log
console.log($('#resumenPreview'));
// Why this isn't working? I'am clueless
$('#resumenPreview').append(x);
};
$scope.pageLoad();
}]);
My guess would be there are multiple divs with id="resumenPreview". But this is clearly the wrong way to handle such things in angular. There shouldn't be dom-manipulation in the controller - directives should take care of dom-related stuff. Put the html-string into the scope and let angular handle the injection into the dom:
instead of $('#resumenPreview').append(x); do $scope.resumenPreview = x;
and in the template do this:
<div class="container">
<div ng-bind-html="resumenPreview"></div>
</div>
Solve it with angularjs for the ng-bind-html to work it's necessary to include
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular-sanitize.js"></script>
and to add 'ngSanitize' as a dependency in the app module configuration. And then just do what #Johannes Reuter posted.
Thanks everybody, Greetings.

Angular expression selecting a directive

I need to render a angular directive, selecting it by appealing to a string previously defined at a variable (usually declared in the controller). Despite such a variable is accessible as an Angular expression, when I try to use for selecting a directive it doesn't work:
<!DOCTYPE html>
<html ng-app="app">
<body ng-controller="TextController">
<!-- item.dir is accessible: -->
<div>Value of item: {{item.dir}}</div>
<!-- It works. the directive "hello" is rendered -->
<div class="hello"></div>
<hello></hello>
Here you should see additional text:
<!-- Doesn't work item.dir is not recognized-->
<!-- as a class -->
<div class="{{item.dir}}"></div>
<!-- as an attribute-->
<div {{item.dir}}></div>
<!-- trying ng-class (it fails)-->
<div ng-class="item.dir"></div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.5/angular.min.js"></script>
<script>
var appModule = angular.module('app', []);
// The directive to render
appModule.directive('hello', function() {
return {
restrict: 'ACE',
template: '<div>works: Transcoded text</div>',
replace: true
};
});
appModule.controller('TextController', function ($scope) {
$scope.item = {dir: 'hello'}; // the name of the directive, the idea is to use it for referring to many future directives.
});
</script>
</body>
</html>
Here's a plunker of the code: http://plnkr.co/edit/tM73sY3vTfPFHmn6iCYE?p=preview
So, what I am missing? How do I achieve to get Angular using string interpolation when using a directive? Thanks!
For directives to work, Angular needs to compile your html (something automatically done when a page is loaded).
Having a way to control freely which directive to instantiate is a bit like pulling the rug under your feet and is atypical. One of the issue is that the compilation "destroy" the internal binding/watchers data and some of the original DOM and thus there is not enough information to "recompile" a DOM node.
NOTE: you cannot change attribute or element names (only attribute values) with angular using this type of binding: {{ }} But ng-class="..." and class="{{...}}" works.
I do not understand what you are trying to achieve exactly. If the intention is really about modifying the item.dir's value and having Angular "reconfigure" your application, it is "possible" but I highly suspect it will induce "state" drawbacks.
Nevertheless, here's a working "hack" that "remembers" the original DOM html and recompiles it when needed. This is done in 2 compilation phases: First phase is to restore the original bindings and a second phase that runs after the $digest cycle so the original bindings finished populating the class name from the scope (i.e. to have item.dir in effect). The drawback of course is if you made modifications to the enclosing DOM, this will wipe them! Alternatively, it might be possible to remember only specific attributes and revert "that" only while keeping other portion of the DOM intact (but might create other issues).
appModule.directive('forceRecompilation', ['$timeout', '$compile', function($timeout, $compile) {
return {
restrict: 'A',
link: function(scope, element, attr) {
var originalHtml = element.html();
scope.$watch(attr.forceRecompilation, function(){
// restore original HTML and compile that
element.html(originalHtml);
$compile(element.contents())(scope);
// wait for all digest cycles to be finished to allow for "binding" to occur
$timeout(function(){
// recompile with bounded values
$compile(element.contents())(scope);
});
});
}
};
}]);
...to be used as an enclosing tag of the section of the DOM to be acted upon. It will "revert & recompile" everything under it when the expression changes. (here "item.dir"):
<div force-recompilation="item.dir">
<div class="{{item.dir}}">
</div>
Plunker: http://plnkr.co/edit/TcMhzFpErncbHSG6GgZp?p=preview
In the plunker, there are 2 directives "hello" and "hello2". Change the text to "hello" and back to "hello2" to see the effect.
EDIT: The following is a directive that allows inserting markup to be compiled as described in a comment below. This is just a slightly modified version of Angularjs - inline directives with ng-bind-html-unsafe
angular.module('bindHtmlExample', ['ngSanitize'])
.controller('ExampleController', ['$scope',
function($scope) {
$scope.test = false;
$scope.dir = "ng-click";
$scope.clicked = function() {
$scope.test = !$scope.test
}
$scope.myHTML =
'I am an <b ng-show="test">invisible</b> HTML string with ' +
'links! and other <em>stuff</em>';
}
])
// modified plunker taken from https://stackoverflow.com/questions/18063280/angularjs-inline-directives-with-ng-bind-html-unsafe
//
// Allows an attribute's value to be evaluated and compiled against the scope, resulting
// in an angularized template being injected in its place.
//
// Note: This directive is prefixed with "unsafe" because it does not sanitize the HTML. It is up
// to the developer to ensure that the HTML is safe to insert into the DOM.
//
// Usage:
// HTML: <div unsafe-bind-html="templateHtml"></div>
// JS: $scope.templateHtml = '<a ng-onclick="doSomething()">Click me!</a>';
// Result: DIV will contain an anchor that will call $scope.doSomething() when clicked.
.directive('unsafeBindHtml', ['$compile',
function($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.unsafeBindHtml);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM element
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}
]);
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.5/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-rc.5/angular-sanitize.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="bindHtmlExample">
<div ng-controller="ExampleController">
<p unsafe-bind-html="myHTML"></p>
(click on the link to see <code>ng-click</code> in action)
</div>
</body>
</html>
Apparently, the tg-dynamic-directive does the trick.

Stop Angular routing links inside ng-view

I'm using Angular JS to dynamically load content like so:
HTML
...
<div ng-controller="MainController">
<div ng-view></div>
</div>
</html>
Angular
(function(){
var app = angular.module('app', ['ngRoute', 'ngAnimate']);
app.config(function($routeProvider) {
$routeProvider.when('/test', {
templateUrl : 'views/test.html',
controller : 'MainController'
});
});
app.controller('MainController', function($scope){ });
})();
This works as expected. However, inside the file test.html I have some links with the href="#" that need to be handled with javascript to do various things. At the moment, Angular is interpreting them with it's routing method and treats them as links to the homepage. How do I stop this and treat the links the way I want?
Example test.html content:
Left
Right
<p>Test content</p>
In a JS file separate from Angular I tried:
$('.slideLeft').on('click',function(){
return false;
});
But it doesn't do the return false, it uses the Angular routing.
You should be using Angular for all your bindings including event bindings. Don't use .on('click'), use ng-click (or .bind if you really need it, but you probably don't).
You can also see from the docs that the <a> directive does nothing if href is empty. Use href="" rather than href="#"
Use href="javascript:void(0)" in anchor attribute and also you should use ngClick instead of binding element using jQuery.

Categories

Resources