How to use ng-model / bind in different views? - javascript

I have a view for my app-header and view for body content ng-view. Basically I have a ng-model input in the profile body and when that is loaded up, I want to bind it to something in the header.
If the ng-model and the binding are in the same view I have no problem, but not sure how to get the binding to go across scopes:
<!-- Main Nav -->
<app-header></app-header>
<div class="content_container">
<!-- angular templating content will be injected here -->
<div ng-view></div>
</div>
Input in profile component
<input ng-model="profile_name" type="text" id="profile_first_name" name="profile_first_name"/>
Header
<div class="user_badge">{{profile_name}}</div>
Header Directive
// Directive for Header
app.directive('appHeader', function () {
// Type of Directive, E for element, A for Attribute
// url of a template
return {
restrict: 'E',
templateUrl: 'shared/navigation/header.html'
};
});
ProfileController
// Controller for Profile
app.controller('ProfileController', function() {
});
// Controller for Complete Registration
app.controller('CompleteRegistrationController', function() {
});
// configure our routes
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
// route : Edit Profile
.when('/profile', {
title : 'Edit Profile',
templateUrl : 'components/profile/edit-profile.html',
controller : 'ProfileController'
});
}]);

I'm guessing you're having a problem with parent/child scoping. There is a quote about ng-model you will see a lot: "If you don't have a dot, you're doing it wrong". This is because of the way prototypal inheritance works.
The solution is to define the model as an object in the parent scope.
<input ng-model="profile.name" type="text" />
and
<div class="user_badge">{{profile.name}}</div>
And in the parent scope:
$scope.profile = {};
This way, when the model is updated the reference to the parent scope is not overwritten, but the model data is updated.
Take a look at the angular guide for scopes if you want to learn more about how it really works: https://docs.angularjs.org/guide/scope
Edit
Here's a snippet showing it working with parent/child scope. It should work exactly the same for ng-view which just adds the controller dynamically.
angular.module('test', [])
.directive('appHeader', function() {
return {
restrict: 'E',
template: '<div class="user_badge">{{profile.name}}</div>'
};
})
.controller('ChildCtrl', function() {
})
.controller('ParentCtrl', function($scope) {
$scope.profile = {
name: 'Test'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="ParentCtrl">
<app-header></app-header>
<div ng-controller="ChildCtrl">
<input ng-model="profile.name" type="text" />
</div>
</div>
</div>

Related

Angular: Calling scope function in partial template included via ng-include

I have an anonymous controller within a directive. That controller, upon a particular event, opens a generic dialog and provides a partial template that is used to add some buttons to the generic dialog via ng-include.
Now within the generic dialog, once the user clicks on any of the provided buttons, I want a particular function in the anonymous controller be called. Any thoughts on how this could be achieved?
Controller:
angular.module('abc')
.directive('xyz', [function() {
return {
restrict: 'E',
scope: {},
controller: ['$scope', function($scope) {
$scope.callThisFunc = function(){};
}]
}
}
Partial template:
<div>
<button label="CANCEL"
ng-click="callThisFunc()">
</button>
</div>
Generic dialog template (different module than the abc module above):
<div ng-include="partial"></div>
Try to use "controllerAs" syntax
angular.module('abc')
.directive('xyz', [function() {
return {
restrict: 'E',
scope: {},
controller: function() {
var vm = this;
vm.callThisFunc = function(){};
return vm;
}],
controllerAs: 'xyzCtrl'
}
}
partial template
<div>
<button label="CANCEL"
ng-click="xyzCtrl.callThisFunc()">
</button>
</div>
generic dialog template
<div ng-include="partial" xyz></div>
</div>

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.

Dynamic adding controller to ng-include

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).

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.

Categories