angular controller-directive data binding - bind property inside object - angularjs

I have two properties in my scope name and city.
If I change city, it reflects in directive. But when I change name, it is not reflecting in directive. I have intentionally passed name from obj object because I am trying to achieve data binding inside object.
Can some one please help how to bind property inside a object, so that the data binding still works.
I think this is where something must be wrong
$scope.obj = { prop: $scope.name };
var myApp = angular.module('myApp',[]);
myApp.directive('passObject', function() {
return {
restrict: 'E',
scope: { obj: '=', city: "=" },
template: '<div>Hello, {{obj.prop}}! from {{city}}</div>'
};
});
myApp.controller('MyCtrl', function ($scope) {
$scope.name="manu";
$scope.city = "hyderabad";
$scope.obj = { prop: $scope.name };
});
<div ng-controller="MyCtrl">
<h3>
Name: <input ng-model="name"/>
</h3>
<h3>
City: <input ng-model="city"/>
</h3>
<pass-object obj="obj" city="city"></pass-object>
</div>

I took your code and added a $scope.$watch to it to update the model $scope.obj on change of $scope.name, works fine now.
`
var myApp = angular.module('myApp',[]);
myApp.directive('passObject', function() {
return {
restrict: 'E',
scope: { obj: '=', city: "=" },
template: '<div>Hello, {{obj.prop}}! from {{city}}</div>'
};
});
myApp.controller('MyCtrl', function ($scope) {
$scope.name="manu";
$scope.city = "hyderabad";
$scope.obj = { prop: $scope.name };
$scope.$watch(
function(){
return $scope.name;
},
function(newVal,oldVal){
$scope.obj.prop = $scope.name;
},
true
);
});
`

Your $scope.obj.prop is binding to a string on another object namely $scope
Your directive will not be notified of the change as the two way binding needs to be done on the property itself.
Example:
var myApp = angular.module('application',[]);
myApp.directive('passObject', function() {
return {
restrict: 'E',
scope: { name: '=', city: "=" },
template: '<div>Hello, {{name}}! from {{city}}</div>'
};
});
myApp.controller('MyCtrl', function ($scope) {
$scope.name="manu";
$scope.city = "hyderabad";
});
<div ng-controller="MyCtrl">
<h3>
Name: <input ng-model="name"/>
</h3>
<h3>
City: <input ng-model="city"/>
</h3>
<pass-object name="name" city="city"></pass-object>
</div>
If you really want to use your structure you can use $watch as Pramit Kundu showed in his answer, OR you can use a function to get the value.
Example 2
var myApp = angular.module('application',[]);
myApp.directive('passObject', function() {
return {
restrict: 'E',
scope: { obj: '=', city: "=" },
template: '<div>Hello, {{obj.prop()}}! from {{city}}</div>'
};
});
myApp.controller('MyCtrl', function ($scope) {
$scope.name="manu";
$scope.city = "hyderabad";
$scope.obj = { prop: function(){ return $scope.name; } };
});
<div ng-controller="MyCtrl">
<h3>
Name: <input ng-model="name"/>
</h3>
<h3>
City: <input ng-model="city"/>
</h3>
<pass-object obj="obj" city="city"></pass-object>
</div>
I think we tend to use $scope.$watch when we are not supposed to seeing that we can overcome the issue by binding correctly to scope or to use a function

Related

Angularjs 2-way data binding push object from directive to controller array

I have a array defined in controller and passing it to directive using two way binding. In directive, i tried to pushed object into that array but it failed.
.controller("test", function($scope){
$scope.myarr =[];
$scope.$watch("myarr", function(newValue, oldValue){
console.log($scope.myarr); //prints empty arr
},true);
});
.directive('ptest', ['$compile', function($compile) {
var object = {value: 'changed value'};
return {
restrict:"E"
scope: {
myarr:"="
},
template : "<div>{{iobj.value}}<div>",
link: function(scope,elem,attr){
myarr.push(object) ;
}
};
}]);
html
<ptest myarr="myarr"></ptest>
Try scope.myarr.push(object); instead of myarr.push(object)
as #George Lee said try scope.myarr.push(object); and also your directive have a mistake. after restrict:"E" you forgot put ,
return {
restrict:"E", // forgot put ','
scope: {
myarr:"="
},
template : "<div>{{iobj.value}}<div>",
// Code goes here
angular.module('app', [])
.controller("test", function($scope){
$scope.myarr =[];
$scope.$watch("myarr", function(newValue, oldValue){
console.log($scope.myarr); //prints empty arr
},true);
$scope.addItem = function(){
var object = {value: 'changed value2'};
$scope.myarr.push(object);
}
})
.directive('ptest', ['$compile', function($compile) {
var object = {value: 'changed value'};
return {
restrict:"E",
scope: {
myarr:"="
},
template : '<div ng-repeat="item in myarr">{{item.value}}<div>',
link: function(scope,elem,attr){
scope.myarr.push(object) ;
}
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="test">
<ptest myarr="myarr"></ptest>
<input type="button" ng-click="addItem()" value="add">
</div>

Angular directive with ng-template

Today I'm trying to develop a popover directive. I don't know why the ng-repeat inside the styles-select directive wich is insered in the popover after click doesn't work(<- Edited it works now)... And I want to get the value of "selectedStyles" in my controller "MyController" without passing it through the directive.
var app = angular.module('MyApp', []);
app.controller('MyController', ['$scope', function($scope) {
$scope.selectedStyles = [];
$scope.$watch('selectedStyles', function(newValue, oldValue) {
console.log(newValue);
});
}]);
app.directive('popover', ['$compile', '$templateCache', function($compile, $templateCache) {
return {
restrict: 'A',
scope: {
header: '#header',
template: '=template'
},
link: function(scope, element) {
element[0].onclick = function (event) {
var popover = document.createElement('div'),
header = document.createElement('h4'),
content = document.createElement('p');
header.textContent = scope.header;
content.innerHTML = $templateCache.get(scope.template);
popover.appendChild(header);
popover.appendChild(content);
document.body.appendChild($compile(popover)(scope)[0]);
scope.$apply();
}
}
};
}]);
app.directive('stylesSelect', ['$compile', '$filter', function($compile, $filter) {
return {
restrict: 'E',
scope: {
selectedStyles: '=selectedStyles'
},
template: '<div ng-repeat="s in styles"><label><input type="checkbox" ng-model="s.selected" ng-change="selectStyle()" /> {{s.label}}</label></div>',
link: function(scope, element) {
scope.styles = [
{label: 'Hipster', selected: false},
{label: 'Hip-Hop', selected: false},
{label: 'Punk', selected: false}
];
scope.selectStyle = function() {
scope.selectedStyles = $filter('filter')(scope.styles, {selected: true});
};
}
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.1/angular.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="MyController">
{{test}}
<button popover template="'popoverContent.html'" header="Select your styles" type="button">Show Popover</button>
<script type="text/ng-template" id="popoverContent.html">
<styles-select selected-styles="selectedStyles"></styles-select>
</script>
</div>
</div>
It gonna make me crazy... Please Help lol
Thank you
Instead of changing values in different scopes, try to use a service with a promise. This way the popover service is more reusable in your application.
var app = angular.module('MyApp', []);
app.controller('MyController', ['$scope', 'popover',
function($scope, popover) {
$scope.selectedStyles = [];
$scope.showStylesSelect = function() {
popover.show({
templateUrl: 'popoverContent.html',
scope: {
header: 'Select your style',
styles: [{
label: 'Hipster',
selected: false
}, {
label: 'Hip-Hop',
selected: false
}, {
label: 'Punk',
selected: false
}]
}
}).then(function(result) {
$scope.selectedStyles = result.selectedStyles;
});
};
$scope.$watch('selectedStyles', function(newValue, oldValue) {
console.log(newValue);
});
}
]);
app.factory('popover', ['$rootScope', '$q', '$compile', '$templateCache',
function($rootScope, $q, $compile, $templateCache) {
function showPopover(options) {
var defer = $q.defer(),
scope = $rootScope.$new(),
popover = document.createElement('div'),
header = document.createElement('h4'),
content = document.createElement('p');
angular.extend(scope, options.scope || {});
scope.close = function() {
popover.parentNode.removeChild(popover);
defer.resolve(scope);
};
header.textContent = options.header || '';
content.innerHTML = $templateCache.get(options.templateUrl);
popover.appendChild(header);
popover.appendChild(content);
document.body.appendChild($compile(popover)(scope)[0]);
return defer.promise;
}
return {
show: showPopover
}
}
]);
app.directive('stylesSelect', ['$filter',
function($filter) {
return {
restrict: 'E',
scope: false,
template: '<div ng-repeat="s in styles"><label><input type="checkbox" ng-model="s.selected" ng-change="selectStyle()" /> {{s.label}}</label></div><button ng-click="close()">close</button>',
link: function(scope) {
scope.selectStyle = function() {
scope.selectedStyles = $filter('filter')(scope.styles, {
selected: true
});
};
}
}
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.1/angular.min.js"></script>
<div ng-app="MyApp" class="ng-scope">
<script type="text/ng-template" id="popoverContent.html">
<styles-select selected-styles="selectedStyles"></styles-select>
</script>
<div ng-controller="MyController" class="ng-scope ng-binding">
<button ng-click="showStylesSelect()">Show Popover</button>
</div>
</div>

Passing scope object to Angular Directive via $compile

I know that if I have a directive in HTML, I can pass objects from my $scope to it:
<mydirective some-data="someData"></mydirective>
...where someData might be JSON object.
Is it possible to pass a reference to someData if I create the directive dynamically and use $compile?
$scope.someData = { firstName: 'John', lastName: 'Doe' };
var link = $compile("<mydirective some-data='???'></mydirective>");
var content = link($scope);
$(body).append(content);
Yes you can pass object to
var myApp = angular.module('myApp',[]);
myApp.directive('passObject', function() {
return {
restrict: 'E',
scope: { obj: '=' },
template: '<div>Hello, {{obj.prop}}!</div>'
};
});
myApp.controller('MyCtrl', function ($scope) {
$scope.obj = { prop: "world" };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"></script>
<div ng-controller="MyCtrl">
<pass-object obj="obj"></pass-object>
</div>
var myApp = angular.module('myApp',[]);
myApp.directive('passObject', function() {
return {
restrict: 'E',
scope: { obj: '=' },
template: '<div>Hello, {{obj.prop}}!</div>'
};
});
myApp.controller('MyCtrl', function ($scope) {
$scope.obj = { prop: "world" };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
<div ng-controller="MyCtrl">
<pass-object obj="obj"></pass-object>
</div>
directive

AngularJS controller inside directive with binding

I have this simple code of my directive:
app.directive('ngModal', function ($parse) {
return {
restrict: 'E',
template: document.getElementById('ng-modal').innerHTML,
replace: true,
controller : "#",
name:"controllerName",
}
})
<ng-modal controller-name="ModalCtrl"></ng-modal>
And this is my controller:
app.controller('ModalCtrl', ['$scope', function ($scope) {
$scope.model = 'default text'
}])
<div ng-controller="ModalCtrl">
<input type="text" ng-model="model">
</div>
I want, that model field inside my Directive will updated automatically. But I see "default text" always inside directive and changed inside controller. How can I bind it?
You have to add a service to keep information between controllers. Controllers are always created per "view" so your ng-modal and div have different controllers in use, this is why model data is not updated between them.
Fast example:
app.service('sharedData', function() {
var sharedData = {
field: 'default text'
};
return sharedData;
});
app.directive('ngModal', function () {
return {
restrict: 'E',
template: '',
replace: true,
controller : "#",
name:"controllerName",
}
});
app.controller('ModalCtrl', ['$scope', 'sharedData', function ($scope, sharedData) {
$scope.model = sharedData;
}]);
<ng-modal controller-name="ModalCtrl">{{model.field}}</ng-modal>
<div ng-controller="ModalCtrl">
<input type="text" ng-model="model.field">
</div>

AngularJS - custom filter, model doesn't update in view

I have a custom filter set up that triggers when the user clicks on a checkbox. I use a directive to render the DOM elements, and attach a listener on the checkbox which when clicked, triggers the filter function that's exposed on the $scope.
The $scope.model which is used in the view should get overwritten by the result of the filter function, and the return object looks ok (e.g. the console.log()) but the view doesn't update. What am I doing wrong?
http://jsfiddle.net/r3pXc/1/
The view:
<body ng-app="app">
<div ng-controller="mainCtrl">
<div list-directive />
</div>
The template:
<script type="text/ng-template" id="list.html">
<input type="checkbox" class="control">
<div ng-repeat="player in model">
Name: {{player.firstName}}, Price: {{player.price}}
</div>
</script>
The module and controller:
var app = angular.module('app', []);
app.controller('mainCtrl', ['$scope', '$filter', function($scope, $filter){
$scope.model = [{ firstName: 'foo', price: 100 }, { firstName: 'bar', price: 50 }, { firstName: 'foobar', price: 0}];
$scope.filter = function() {
$scope.model = $filter('listFilter')($scope.model);
console.log($scope.model);
}
}]);
The directive:
app.directive('listDirective', function(){
return {
restrict: 'AE',
templateUrl: 'list.html',
link: function($scope, iElm, iAttrs, controller) {
iElm.bind('click', function(e){
var el = angular.element(e.target);
if (el.hasClass('control')) {
$scope.filter();
};
});
}
};
});
And the filter:
app.filter('listFilter', function(){
return function(input) {
var results = [];
angular.forEach(input, function(val, key){
if (val.price != 0) {
results.push(val);
}
});
return results;
}
});
Need to manually call the digest cycle with $apply(), because I don't listen on the elements with ng- event handlers:
$scope.filter = function() {
$scope.model = $filter('listFilter')($scope.model);
$scope.$apply();
}

Resources