Dynamic adding controller to ng-include - javascript

Is that a dynamic way to change ng-include controller?
My app allows users to create page some content and controllers. I can change ng-include src but I don't know how to dynamic associate a new controller. The following code isn't working:
<div ng-app="MyApp">
<div ng-controller="ParentController">
<select ng-model="currentItem" ng-options="item as item.url for item in items track by item.url">
</select>
{{ currentItem }}
<div ng-include src="currentItem.url" ng-controller="currentItem.controller"></div>
</div>
</div>
And I have the following JS:
var app = angular.module("MyApp",[]);
app.controller('ParentController', ['$scope',function($scope){
$scope.items = [{
url: 'page1.html',
controller: 'Page1Controller'
},
{
url: 'page2.html',
controller: 'Page2Controller'
}];
$scope.currentItem = {};
}]);
app.controller('Page1Controller', ['$scope',function(){
alert('Page1');
}]);
app.controller('Page2Controller', ['$scope',function(){
alert('Page2');
}]);

I've done by using a directive instead:
<div ng-include src="currentItem.url" dyn-controller="currentItem.controller"></div>
JS Directive:
app.directive('dynController', ['$compile', '$parse',function($compile, $parse) {
return {
restrict: 'A',
terminal: true,
priority: 100000,
link: function(scope, elem, attrs) {
// Parse the scope variable
var name = $parse(elem.attr('dyn-controller'))(scope);
elem.removeAttr('dyn-controller');
elem.attr('ng-controller', name);
// Compile the element with the ng-controller attribute
$compile(elem)(scope);
};
}]);
The trick here is watching for attribute changes, add ng-controller and then compile the element.
Thanks for
How to watch property in attrs of directive
and
Dynamic NG-Controller Name

The following array of items is an array of plain objects that have two fields: url and controller, both containing string values.
$scope.items = [{
url: 'page1.html',
controller: 'Page1Controller'
},... ]
What you really need is a reference to the function and that can be achieved with the $injector service.
Eventough this might work, I don't get why you aren't using the ParentController to alter the object in your array (or a directive).

Related

Angular: Variable template inside directive

In my Angular template I use an attributive directive as follows:
HTML:
<div id="my-template-one" my-template-directive></div>
JS:
// ...
.directive('myTemplateDirective', ['myconfig', function (myconfig) {
return {
templateUrl: myconfig.TEMPLATE_PATH + 'my-template-one.html',
controller: function ($scope, $rootScope) {
// code
},
controllerAs: 'dir'
}
}]);
For including another template, my-template-two.html, on another page, I would like to use the same directive. I do not want to duplicate the directive. How can I pass the template as an variable?
HTML on another page:
<div id="my-template-two" my-template-directive></div>
My goal is that somehow I can tell my directive to render my-template-two.html when this HTML is called.
The templateUrl property value may be a function which takes two arguments tElement and tAttrs and returns a string value:
app.directive('myTemplateDirective', ['myconfig', function (myconfig) {
return {
templateUrl: function (tElem, tAttrs) {
var template = "my-template-one.html";
if (tAttrs.use) {
template = tAttrs.use;
};
return myconfig.TEMPLATE_PATH + template;
},
controller: function ($scope, $rootScope) {
// code
},
controllerAs: 'dir'
}
}]);
Usage
<div my-template-directive use="my-template-two.html"> </div>
For more information, see AngularJS Comprehensive Directive API -- template

Updating ngModel of custom element from inside angular controller instead of Angular directive

I have a directive something like the following for a custom control
myApp.directive('myControl', function ($filter) {
return {
restrict: 'E',
scope: { ngModel: "=" },
templateUrl: 'some template path',
controller: 'myControlController'
}
});
I define the all the functionality of this control in a separate controller block:
myApp.controller('myControlController',['$scope', function($scope){
$scope.value=$scope.ngModel;
//$scope.ngModel='newValue';
}]);
I have a parent controller within which I create a myControl as follows:
myApp.controller('componentController',['$scope', function($scope){
$scope.myModelValue='OriginalValue';
}]);
The following is the html content of the parent page:
<html>
<body>
<div ng-controller="componentController">
<my-control ng-model="myModelValue"></my-control>
{{myModelValue}}
</div>
</body>
</html>
Now, I wish to update the value of the ngModel to 'newValue' from the original value of 'OriginalValue' using something like
//$scope.ngModel='newValue';
Is there any way to achieve this other than doing it from a directive or using a $parent operation.

How to pass a variable from a directive (ngModel) into the html outside of the directive

I've got a custom directive with an html template and it basically is a menu option. When a user makes a selection it updates the ng-model variable within the directive.
But I would like for the ng-model variable within the directive passed outside of the directive into the html page.
Here's the code snippets:
Directive:
(function() {
'use strict';
angular
.module('myModule')
.controller('myController', ['$scope', function($scope) {
$scope.sortByOptions = [
{ value:'red', displayText:'Redder' },
{ value:'blue', displayText:'Bluer' },
{ value:'gold', displayText:'Golder' },
{ value:'brown', displayText:'Browner' }
];
}]
)
.directive('myDirective', myDirective);
myDirective.$inject = [];
function myDirective() {
return {
restrict: 'A',
templateUrl: 'mydirective/sorting.html',
}
}
})();
HTML Template for the directive:
<select ng-model="sortBy" ng-options="sortByOption.displayText for sortByOption in sortByOptions track by sortByOption.value"> </select> {{sortBy.value}}
HTML of the Page:
<div class="col-md-8 form-inline" my-directive>
</div>
<!-- need ng-model variable from my-directive passed into sortBy --> <!-- it's being called on same page. I turned a menu options into a directive to save from copying/pasting same code everywhere. when the menu option gets selected it populates a list which is the li you see below -->
<li ng-repeat="object in objects | filter: searchTool | orderBy: (sortAscending ? '' : '-') + sortBy">
<div class="plank">
<div class="plank-header">
</div>
</div>
</li>
As you can see I'm trying to pass ng-model="sortBy" value from the directive which is chosen by the user into other parts of the page called sortBy that is within the li.
It would be awesome if someone can give an example of what they did.
I've done something similar by essentially exposing the variable in your directive to your controller. You can do this by passing a function from your controller into your directive such that that function gets called and essentially sets a variable in your controller. Something like this.
<div mydirective call-outside-function="setSortBy".....>
mycontroller function(...) {
$scope.setSortBy = function(sb) {
$scope.localSortBy = sb;
};
}
mydirective .....
link: function(scope,element,attrs) {
scope.$watch('sortBy',function(newval) {
attrs.callOutsideFunction(newval);
});
}
Probably not the most elegant solution but it works
I did the following:
1) added a scope.$watch to allow my directive to listen for a change in that variable on the DOM. Then it will set the new value in the controller
2) Added the controller into the directive. I originally forgot to add a line in the function colorSorter to return the controller
3) I did not need to modify the directive's html template or the main html template for the page to get it working.
Here is the directive:
(function() {
angular
.module('myModule')
.controller('sortController', ['$scope', function($scope) {
$scope.sortByOptions = [
{ value:'red', text:'Redder' },
{ value:'blue', text:'Bluer' },
];
$scope.sortBy = {value: undefined}
}]
)
.directive('colorSorter', colorSorter);
colorSorter.$inject = [];
function colorSorter() {
return {
restrict: 'A',
templateUrl: 'app/color-sorter/color-sorter.html',
controller: 'sortByController',
link: function (scope) {
scope.$watch('sortBy.value', function (value) {
console.log(value);
})
}
}
}
})();

How can i change array in directive, and then reflect that change in my controller?

I made directive with isolated scope with "=" method, in that directive i pass empty array, then i push data on that array.... How that change can be reflected on original array in my controller?
Here is the example:
angular.module('myModule').controller('MyController', ['$scope', function($scope) {
$scope.test = [];
}]);
angular.module('myModule').directive('mydirective', function() {
return {
scope: {
test: "=",
bread: "="
},
restrict: 'E',
link: function(scope, element, attribute) {
scope.test.push('one more')
},
replace: true,
templateUrl: 'some template'
};
});
HTML
<div ng-controller='MyController'>
<mydirective test='test'></mydirective>
<div ng-bind='test'> </div>
</div>
When i push something on array i dont have a reflection of that in my controller.
How can i fix that?
Here's how to do what you are trying to achieve.
HTML
<!-- myCtrl contains the original array, which we push here to the scope as 'ctrl' -->
<div ng-controller='myCtrl as ctrl'>
<!-- we pass your directive 'ctrl.test', which tells angular to two-way bind to the
test property on the 'ctrl' object on the current scope -->
<mydirective test='ctrl.test'>
<!-- we're inside the isolate scope: test here refers to mydirective's idea of test,
which is the two-way bound array -->
<div ng-bind='test'></div>
</mydirective>
</div>
JS
angular.module('app', [])
.directive('mydirective', function() {
scope: {
test: '='
},
link: function($scope) {
$scope.test.push('one more');
}
})
.controller('myCtrl', function() {
this.test = [];
});
Any alterations to the array will now be reflected in the ng-bind. Please note that it is bad practice to place primitives on $scope without being part of an object (due to the mechanics of prototypical inheritance) so you'd want to change $scope.test to something else.

Iterate config array in html template via Angular directives?

I've got into AngularJS recently and trying to create a registration form where I would fill country list from my JavaScript config that is widely used elsewhere, thus I keep it as JS object.
I've been trying to use ng-repeat-start and *-end on my select input, but it fails.
The main question is, how do I load countries array and iterate it in my template?
Edit: 30.11.2014 - Better examples
HTML:
<div class="form-group">
<label for="phone">Country</label>
<select-country ng-model="credentials.country"></select-country>
<pre>{{credentials.country}}</pre>
</div>
File:
/public/directives/countrySelector.directive.js
Directive contents:
'use strict';
angular.module('app', [])
.value('countries', [
{name: 'Afghanistan', code: 'AF'},
{name: 'Åland Islands', code: 'AX'}
])
.directive('selectCountry', ['countries', function (countries) {
function link (scope, element, attrs) {
scope.countries = countries;
}
return {
template: '<select ng-options="country[1] as country[0] for country in countries"' +
' ng-model="credentials.country">' +
'</select>',
link: link,
scope: {
credentialsCountry: '='
}
};
}]);
just replace country[0] and country[1] with country.code and country.name
http://plnkr.co/edit/OIGGitze5LLehDes2MQ8?p=preview
or i missed something?
I don't think you need to make a new directive for this. There is already a built in select directive for angular, so you are making more work for yourself than necessary. So what you could do is inject that service into your controller for the page, then bind the service to the scope of the controller like you did for the directive. It would end up looking something like:
angular.module('app', [])
.value('countries', [
{name: 'Afghanistan', code: 'AF'},
{name: 'Åland Islands', code: 'AX'}
]);
and the you have a controller
app.controller('whateverTheControllerIs', ['countries', '$scope', function (countries, $scope) {
$scope.countries = countries;
});
and then your scope is available to the template
<div class="form-group">
<label for="phone">Country</label>
<select ng-options="country.name for country in countries" ng-model="credentials.country"></select>
<pre>{{credentials.country}}</pre>
</div>
As a side note: if you would like to find out about best practices for the latest Angular 1.* version. Read everything Todd Motto says.
It's a good idea to create directives for things that could be commonly reused, like a country selector. You want to inject your countries into a directive where you can then iterate over it with ng-options. Plunker: http://plnkr.co/edit/zfkeLNQ0LHxR7FB0nM18?p=preview
.directive('selectCountry', ['countries', function (countries) {
var directive = {
template: '<select ng-options="country[1] as country[0] for country in countries"' +
' ng-model="ngModel">' +
'</select>',
link: link,
scope: {
ngModel: '='
}
};
return directive;
function link (scope, element, attrs) {
scope.countries = countries;
}
}]);

Categories