Can I use one ng-app inside another one in AngularJS - angularjs

I have two ng-app
like ;
<div ng-app="app1" >
somexpression
<div ng-app="app2">
some more expression
</div>
</div>
is there any way to make it work?
when I make a nested ng-app it doesn't work
I know that I can use two different controller but I don't want to use two controllers
---- EDIT -----
The thing is;
angular.module('AppName', [
'angular-carousel'
])
SO I need somehow to change this ng-app to directive

From the AngularJS document, the answer is no
http://docs.angularjs.org/api/ng/directive/ngApp
AngularJS applications cannot be nested within each other.
And if not nested, then it's OK, someone already asked this question, refer here:AngularJS Multiple ng-app within a page
and the AnguarJS document
http://docs.angularjs.org/api/ng/directive/ngApp
Only one AngularJS application can be auto-bootstrapped per HTML document. The first ngApp found in the document will be used to define the root element to auto-bootstrap as an application. To run multiple applications in an HTML document you must manually bootstrap them using angular.bootstrap instead.

I found one tricky solution for this problem. The idea is that the "host" application have to somehow jump over the root element of nested application. I used directive for this:
angular.module("ng").directive("ngIsolateApp", function() {
return {
"scope" : {},
"restrict" : "AEC",
"compile" : function(element, attrs) {
// removing body
var html = element.html();
element.html('');
return function(scope, element) {
// destroy scope
scope.$destroy();
// async
setTimeout(function() {
// prepare root element for new app
var newRoot = document.createElement("div");
newRoot.innerHTML = html;
// bootstrap module
angular.bootstrap(newRoot, [attrs["ngIsolateApp"]]);
// add it to page
element.append(newRoot);
});
}
}
}
});
Example simple app:
// module definition
angular.module("testMod1",[])
.service("moduleService", function ModuleService() {
this.counter = 0;
this.getCounter = function() {
return this.counter;
};
this.incCounter = function() {
this.counter += 1;
}
})
.controller("ModuleCtrl", function(moduleService) {
this.getValue = function() {
return moduleService.getCounter();
};
this.incValue = function() {
moduleService.incCounter();
};
});
Now in the markup we can use the ng-isolate-app:
<!-- App instance 1 -->
<body ng-app="testMod1">
<div ng-controller="ModuleCtrl as ctrl">
{{ctrl.getValue()}}
<button ng-click="ctrl.incValue()">Click</button>
<!-- App instance 2 -->
<div ng-isolate-app="testMod1">
<div ng-controller="ModuleCtrl as ctrl">
{{ctrl.getValue()}}
<button ng-click="ctrl.incValue()">Click</button>
<!-- App instance 3 -->
<div ng-isolate-app="testMod1">
<div ng-controller="ModuleCtrl as ctrl">
{{ctrl.getValue()}}
<button ng-click="ctrl.incValue()">Click</button>
</div>
</div>
</div>
</div>
</div>
</body>
Working example on plnkr
This works in simple cases, I do not know how this will work on complex applications.

You can't use one ng-app inside another one in angularjs.
because AngularJS applications cannot be nested within each other.
https://docs.angularjs.org/api/ng/directive/ngApp

Related

AngularJS - How to change another image when principal image change

I'm trying to develop a solution through AngularJS where when the user changes the profile image, all other photos based on their profile are also changed.
How can I start developing this feature?
Thanks in advance!
[Solution]
With the help of the #wolfman6377, I can understand the functionality and develop the solution for what I need. I'll leave an example in CodePen.
CodePen
var myApp = angular.module('myApp',[]);
myApp.controller('myController', function() {
this.myVar = 'https://robohash.org/asd'
this.updatePicture = function() {
this.myVar = 'https://angular.io/resources/images/logos/angular/angular.png';
};
});
Thank you, #wolfman6377.
You are using a jQuery-like strategy. jQuery element selector will not be able to change the value of myVar.
I would recommend going through some basic tutorials on angular1. But in a nutshell, you need to:
Do not grab elements and change attributes
add a module to the ng-app value
Use ng-controller in the template
Add ng-click to the button to update the value of myVar
template
<body ng-app="myApp">
<div id="var" ng-controller="myController as vm">
<img ng-src={{vm.myVar}} />
<button ng-click="vm.updatePicture()">Click me</button>
</div>
</body>
controller:
angular
.module('myApp', [])
.controller('myController', function() {
this.myVar = 'https://www.w3schools.com/angular/pic_angular.jpg'
this.updatePicture = function() {
this.myVar = 'https://angular.io/resources/images/logos/angular/angular.png';
};
});

Using KendoUI methods in AngularJS

How can I use the methods provided by KendoUI widgets in an AngularJS application?
For the configuration of the widgets the directive attributes are used within the markup via k- prefixes, I know.
But in the documentation, there are a lot of methods assigned to the widgets.
For example the kendo-mobile-tab-strip widget has a switchTo method.
The example shows to use it this way (without angular)
var app = new kendo.mobile.Application();
function onClick() {
var tabstrip = app.view().footer.find(".km-tabstrip").data("kendoMobileTabStrip");
tabstrip.switchTo("#bar"); //activate "bar" tab
}
How can I access the app variable in an Angular controller, when I just use a <kendo-mobile-application> directive to initialize the app?
Is there any other (proper) way of using the widget methods?
I'm curious of the best practice here, because this all feels not like Angular...
When you supply a value to the widget's attribute (or to a k-scope-field attribute), that becomes the name of the scope property which you can use to get a reference to the widget.
HTML
<div ng-controller="MainCtrl">
<div kendo-grid="myGrid"></div>
<!-- or -->
<div kendo-grid k-scope-field="myGrid"></div>
</div>
Controller
angular.controller("MainCtrl", function($scope) {
// the widget is accessible from the scope
$scope.myGrid.doSomething();
});
You're right, calling methods on widget isn't like Angular. So in most cases you want to be putting this in your own directives, wrapping kendo's widgets. This lets you keep DOM specific code out of your application controllers and contained in directives, where they belong.
HTML
<div ng-controller="MainCtrl">
<my-grid-directive></my-grid-directive>
</div>
Directive
angular.controller("myGridDirective", function() {
return {
template: "<div kendo-grid='myGrid'></div>",
link: function (scope, element, attrs) {
scope.myGrid.doSomething();
}
};
});
I never using kendo mobile application so it might be different but in general to access the kendo object in the $scope object by adding identifier in kendo attribute per example below.
<div ng-app="app" ng-controller="MyCtrl">
<input kendo-datepicker="datePicker" k-on-change="onChange()">
</div>
<script>
angular.module("app", [ "kendo.directives" ]).controller("MyCtrl", function($scope) {
$scope.onChange = function() {
alert($scope.datePicker.value());
};
});
</script>
http://docs.telerik.com/kendo-ui/AngularJS/introduction#getting-widget-references

how to get clean version of the html in angularjs

I wrote an application that uses angularjs for data-binding.
The application is some sort of visual html builder.
When the user is done, I want to allow him to export the HTML.
Since I did massive usage in angularjs, its attributes are all over the place, and the generated HTML is ugly.
Is there anyway to get clean version of the HTML?
this example will export the ugly html: http://jsfiddle.net/ga25hep2/
<div ng-app="myApp">
<div id="the_html" ng-controller="MyCtrl">
Hello, {{name}}!
</div>
<button onclick='exportMe()'>export</button>
</div>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.name = 'Superhero';
}
function exportMe(){
alert(document.getElementById('the_html').outerHTML)
}
If you can update angular to a version after 1.3 (you really should, there are lots of other benefits, too), then you can disable all of the ng- class spam by disabling debug info in your app:
myApp.config(function ($compileProvider) {
$compileProvider.debugInfoEnabled(false);
});
You can see the result in an updated fiddle here.
Note that I changed the structure of things slightly but the functionality is the same.
I don't think it exists a native way to do this. AngularJS is not about such kind of work.
Just write a script that parses your exported code and remove each ng-* attribute
We need to iterate through all the dom nodes and remove the ng related attributes and ng related classes.
function exportMe(){
var targetEl = document.getElementById('the_html');
var nodes = targetEl.querySelectorAll('*');
nodes = Array.prototype.slice.call(nodes); // convert NodeList to array
nodes.unshift(targetEl); // include the root node while iterating.
nodes.forEach(function (node) {
Array.prototype.slice.call(node.attributes).forEach(function (attr) {
var classes = Array.prototype.slice.call(node.classList); // conver classList to array
classes = classes.filter(function (cls) {
return !(cls.indexOf('ng-') === 0 || cls.indexOf('data-ng-') === 0);
});
// remove ng related classes
node.setAttribute('class', classes.join(' '));
// remove ng related attributes
if(attr.name.indexOf('ng-') === 0 || attr.name.indexOf('data-ng-') === 0) {
node.removeAttribute(attr.name);
}
})
});
alert(targetEl.outerHTML)
}
Here is a solution: http://jsfiddle.net/ga25hep2/2/

How can I generate on the fly directives in AngularJS?

I want to be able to take an array of strings, and then create directives based upon those strings. Either element or attribute will work fine but can't seem to get it working.
<div ng-repeat="module in modules.directives">
<div {{module.directive}}></div>
</div>
<div ng-repeat="module in modules.directives">
<{{module.directive}}></{{module.directive}}>
</div>
<div ng-repeat="module in modules.directives">
<{{module.directive}} />
</div>
Can't get any of these to work. Any ideas?
You could define a directive that would proxy another directive like so
<div proxy="'ng-if'" proxy-value="'foo'"></div>
<div ng-init="n='ng-if'; v='foo';" proxy="n" proxy-value="v"></div>
that would both be equivalent to
<div ng-if="foo"></div>
the proxy directive definition would be
app.directive('proxy', function ($parse, $injector) {
return function (scope, element, attrs) {
var nameGetter = $parse(attrs.proxy);
var name = nameGetter(scope);
var value = undefined;
if (attrs.proxyValue) {
var valueGetter = $parse(attrs.proxyValue);
value = valueGetter(scope);
}
var directive = $injector.get(name + 'Directive')[0];
if (value !== undefined) {
attrs[name] = value;
}
return directive.compile(element, attrs, null)(scope, element, attrs);
};
});
This is actually kind of a fun directive to write once in a life. :-) but it lacks a lot of the native directive features (for instance template, templateUrl, controller, etc). All those features are available in the original Angular source code in a private function called applyDirectivesToNode, so it is easy to copy/paste some parts, but ugly... I have written a demo matching your usecase here.
Another solution, if you don't mind your proxied directive does not share the same element as the proxy directive's one, would be to $compile a dynamic template at runtime that you would include. Here is a demo.
ng-include can help you. The approach would be to define a template for each of the directives. Something like this
<script type="text/ng-template" class="template" id="test-module">
<test-module></test-module>
</script>
Then in ng-repeat do
<div ng-repeat="module in modules.directives">
<ng-include src="module.directive">
</div
If the template id matches with module.directive that directive would get rendered.

Dynamically add existing controller into another controller with AngularJS

in my app I have a wrapper controller that handles some properties dynamically based on other-controllers within it. everything works like a charm if the other-controllers are present/static on load, but as soon as I'm trying to make them dynamic, they stop working.
It was my understanding that the $rootScope is available from everywhere within the app, is that not true?
my JS looks like this:
var webApp = angular.module("webApp",[]);
webApp.controller("ControllerA", function($scope, $rootScope){
$rootScope.cnt = 0;
$rootScope.cntPlusPlus = function(){
$rootScope.cnt++;
};
$rootScope.controllerBs = [];
var template = $(".controller-b").html();
$scope.addControllerB = function(){
$rootScope.controllerBs.push(template);
};
});
webApp.controller("ControllerB", function($scope, $rootScope){
$scope.cntPlusPlus = function(){
console.log("overwrite plus plus");
}
});
Full example: http://plnkr.co/edit/tAcv1F9y7t9q9XsQ1EFL?p=preview
I know that this would be probably better with directives, but is there any way to make it work with Controllers?
thanks for the help
Don't try to access the DOM from controller code. Never. It is very bad practice which breaks AngularJS conventions and eventually provides you with bad architecture. This also means you should not create any DOM elements manually from a controller.
Better to manipulate with the scope itself, not with its visual representation. You can add new models to scope on your button's click, which will be translated to new elements by ng-repeat directive, each with its own controller (remember controllers are instances, not singletons, so that they have separated life cycles).
You might want to make use of <script type="text/ng-template"> and ng-include here instead of hidden divs.
Try to avoid using $rootScope when possible - it is global state which can be dangerous.
It might look like this then (plunker):
HTML:
<div class="controller-a" ng-controller="ControllerA">
Controller A
<div>
<button ng-click="cntPlusPlus()">cnt++</button> CNT: {{cnt}}
</div>
<button ng-click="addB()">Add B</button>
<div ng-repeat="B in Bs">
<div ng-include="'b-template'"></div>
</div>
</div>
<script type="text/ng-template" id="b-template">
<div ng-controller="ControllerB">this is controller b: <button ng-click="cntPlusPlus()">cnt++</button></div>
</script>
JS:
var webApp = angular.module("webApp",[]);
webApp.controller("ControllerA", function($scope){
$scope.cnt = 0;
$scope.cntPlusPlus = function(){
$scope.cnt++;
};
$scope.Bs = [];
$scope.addB = function(){
$scope.Bs.push({});
};
});
webApp.controller("ControllerB", function($scope){
$scope.cntPlusPlus = function(){
console.log("overwrite plus plus");
$scope.$parent.$parent.$parent.cnt++; //should be moved to service
}
});
</script>

Resources