Updating Scope from a Directive - angularjs

I have defined a scope in my directive.
scope.selection = 3
When I apply the directive to an element,
<div myDirective></div>
its template would print the scope var correctly.
<span>{{selection}}</span> //prints: 3
but when I update the scope variable in the directive it does not update in the view.
element.bind("keydown", function(event){
if(event.which === 38){ //up arrow
scope.selection--;
console.log(scope.selection); //logs the updated value but view stays the same
}
}
Can I bind scopes from a directive?
EDIT:
If I view a different browser tab then go back to the app the view is then updated.

Related

Link scopes of element and attribute directives

I have element directive, let's say "card". It has value. Also i have attribute directive, let's call it "clickable", that should bind to existing directive scope and add +1 to it's value.
The problem is that scopes of element and attribute directives are different and i need to do scope.$apply() after click.
el.on('click', (event) => {
ctrl.value++;
console.log('clicked', ctrl.value);
// i need scope.$apply(); here to update <card>'s value
});
I want to attribute directive to not have it's own scope, relying on parent's instead.
try to click on those inputs: http://codepen.io/Fen1kz/pen/GZrgBx?editors=0010

Target ng-if outside controller

I have a feedback feature on my app,
#feedback
%h3.button
%a{"ui-sref" => ".feedback", "ng-click" => "feedbackActive=!feedbackActive;"}
Feedback
#feedback-container{"ng-if" => "feedbackActive"}
%div{"ui-view" => "feedback"}
The #feedback-container has a ng-if so that the content is only loaded when needed. The %a has a ng-click that toggles between true/false for the the feedbackActive state.
This works fine. Howoever in my ui-view I load a template. In that template is a send button that has a send() function linked to the feedbackCtrl,
$scope.send = function(){
console.log ('send')
console.log ($scope.feedbackForm)
createFeedback.create({
name: $scope.feedbackForm.name,
feedback: $scope.feedbackForm.feedback
})
$scope.feedbackActive = false;
}
It runs the code fine, but doesn't give the feedbackActive the false value so nothing happens.
How do remove toggle the ng-if from outside the controller?
This is a classic scoping issue. Your controller's scope is a child of the scope of the ng-if directive. One option is to use $scope.$parent to set the variable on the parent scope.
$scope.send = function(){
console.log ('send')
console.log ($scope.feedbackForm)
createFeedback.create({
name: $scope.feedbackForm.name,
feedback: $scope.feedbackForm.feedback
})
$scope.$parent.feedbackActive = false;
}
The other option is to take advantage of prototypical inheritance. In the parent controller, initialize an object.
$scope.x = {}; //parent scope
In the view controller, set properties on the inherited object.
$scope.x.feedbackActive = false; //child scope
And of course in your HTML
<feedback-container ng-if="x.feedbackActive">

ionic, pass scope variable to popover scope

I would like to pass a variable from the view scope ($scope.sort) into the popover scope.
Once set in the popover, the variable 'goes out' of it with no issue though, with code below in$scope.closeSortPopover`.
I thought the popover scope was the same as the scope of the controller it comes from.
But it seems it's not the case.
Where is the popover scope ?
If I understand correctly from here the popover scope is a child of the controller scope. So how can I access it... ?
FYI my popover is a list of <ion-radio>s offering different sorting opions for the big list in my main view.
html:
<button ng-click="openSortPopover($event, sort)">
controllers.js
$ionicPopover.fromTemplateUrl('templates/computeSortPopover.html', {
scope: $scope
}).then(function(popover) {
$scope.sortPopover = popover;
});
$scope.openSortPopover = function($event, sort) {
$scope.sortPopover.show($event);
};
$scope.closeSortPopover = function(sort) {
$scope.sortPopover.hide();
$scope.sort = sort;
};
In the controller js, you are assigning the scope of the controller to the scope of the popover. Thus all the methods in the controller scope are accessible via the popover view. In other words, both the controller view and popover view share the same scope.
In fact you might think this a violation of mvc as one controller cannot own two views, but with angular, you actually can't otherwise.
To create isolated scope, your only option is directives.
I discovered that due to javascript inheritence, the scope variable must be contained into an object to be passed to inherited scopes.
So instead of this:
$scope.sort = ....
I declared this in my controller:
$scope.data={};
$scope.data.sort = ....
and used in my html (both in the initial view and in the popover templates):
data.sort
Documented here apparently: https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance

Two-way binding within nested directives and ui-select

Problem I have been working on:
The original problem (which only addressed one-way binding) can be found here:
Unable to pass/update ngModel from controller to directive
I have managed to get the one-way binding from inside the directive out to the view working. But if I want push in a selection when the page loads, for example, I cannot get the two-way binding to function. I can see that the ng-model inside the first directive is getting the data, but I tried various scope settings with the child directive and either it breaks it or it does the same thing as before - nothing.
I have a basic $watch function set up, so that when I push a simple object into the binding that is attached to ng-model in the view, the watcher assigns the $viewValue to the directive's scope object. It does this, and the directive responds only by having any existing selection wiped out, even though I can clearly see the objects inside ng-model binding assigned to ui-select.
Here is the watch function:
scope.$watch(function() {
return ngModel.$viewValue;
}, function(newVal) {
console.log(newVal, scope.options);
scope.options.selected = newVal;
});
I use this function to update the view whenever we interact with the ui-select (which works fine):
scope.changer = function() {
ngModel.$setViewValue(scope.options.selected);
console.log(scope.options.selected);
};
A Plunker for tinkering
So the expected behavior is:
selections from the ui-select should be displayed in the select, and also get passed to the view
by clicking the 'Change' button, data should be displayed in the view, and get passed to the ui-select
The situation was that I had a directive with ui-select inside it. The way I was able to get data from the main controller scope through the directive scope and down to ui-select's scope was by putting a watch function on ngModel.$viewValue in the link function of the directive.
I then assigned that new value, whenever there was a change, to an object on that directive's scope, to hold it for me. Then I set a watch function in the link function of ui-select to watch scope.$parent.myVal so that if anything was ever pushed to that variable from the main controller, ui-select would see it. When that happened, I would assign that new value to $select.selected which is the container for selected items in the ui-select directive.
It should be noted that ui-select uses scope: true in it's directive, and thus becomes a child of whatever scope it is instantiated in, which allows you to access the scope of it's parent with $parent.
Watch function for the directive:
scope.$watch(function() {
return scope.$parent.myVar;
}, function(newVal) {
$select.selected = newVal;
})
Watch function to be added to ui-select:
scope.$watch(function() {
return ngModel.$viewValue;
}, function(newVal) {
scope.myVar = newVal;
})
Plunker Demo

Angular Directive Two-Way Binding Issue

I am creating a custom input directive for some added features.
app.directive('myTextInput', [
function () {
var directive = {};
directive.restrict = 'A';
directive.scope = {
textModel: '='
};
directive.link = function ($scope, element, attrs) {
// FUnctionality
};
directive.template = '<input ng-model="textModel" type="text">';
return directive;
}]);
and used the isolated scope property textModel for two way binding.
And the parent scope HTML is:
<div my-text-input text-model="myScopeVariable"></div>
<span>TYPED : {{myScopeVariable}}</span>
The controller scope has variable $scope.myScopeVariable=""; defined.
When i do this.
what ever typed in the directive input is able to print in the <span> tag.
Which tells it is updating.
The issue is.
in the parent scope. i have a method that will execute on a button click.
$scope.doSomething = function(){
console.log($scope.myScopeVariable);
};
On click on the button. the log is empty. which is supposed to be what is typed in the directive input.
THIS IS STRANGE
Now if define the scope variable as
$scope.myScopeVariable = {key:''};
and use in HTML as
<div my-text-input text-model="myScopeVariable.key"></div>
<span>TYPED : {{myScopeVariable.key}}</span>
then it is working every where. Even in the previously said function doSomething().
Any idea what happening here ?
This is happen because myScopeVariable contain value as String.
So if you change the value of from textbox of directive. It wont be reflected.
And in second method, you are referring Object. so whenever you change value of object , its relevant watch method is called. And value will be updated.
my-text-input is a directive, so he has his own scope, so the model myScopeVariable is not watch by the parent.
try with this :
<div my-text-input text-model="$parent.myScopeVariable"></div>
<span>TYPED : {{myScopeVariable}}</span>
It's not the best practice but it might works.

Resources