AngularJS - Create a directive that uses ng-model and md-autocomplete - javascript

I am trying to create a directive for md-autocomplete. I tried using the answer provide by AngularJS - Create a directive that uses ng-model, but it does not work for me.
My CodePen is here: http://codepen.io/colbroth/pen/QyMaQX/?editors=101.
The code is based on the Angular material demo for autocomplete at https://material.angularjs.org/latest/demo/autocomplete. I have multiple pages, which need an autocomplete field to select a state. I do not want to repeat the code for each web page component.
state-directive takes an md-autocomplete input, I need demoCtrl.selected state to reflect the same value in both cases. But when I update the input element, the state-directive does not reflect this and vice versa.
<input ng-model="demoCtrl.selectedState">
<state-directive ng-model="demoCtrl.selectedState">...</state-directive>

You are on the right track. Your problem is that your model is a string - a primitive in javascript but a ngModel always needs to be an object if you want to avoid these kind of problems.
Scope inheritance is normally straightforward, and you often don't even need to know it is happening... until you try 2-way data binding (i.e., form elements, ng-model) to a primitive (e.g., number, string, boolean) defined on the parent scope from inside the child scope. It doesn't work the way most people expect it should work. What happens is that the child scope gets its own property that hides/shadows the parent property of the same name. This is not something AngularJS is doing – this is how JavaScript prototypal inheritance works. New AngularJS developers often do not realize that ng-repeat, ng-switch, ng-view and ng-include all create new child scopes, so the problem often shows up when these directives are involved. (See this example for a quick illustration of the problem.)
This issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-models.
Taken from Understanding-Scopes
It also links to this Youtube video - 3 minutes of well invested time
function DemoCtrl() {
var self = this;
self.state = {
selected: "Maine"
};
}
Fixed codepen

Related

Data binding in directive isolated scope

I want to create a directive with isolated scope, but I'm not able to get it working.
jsFiddle
I want to isolate age model in a directive scope. I want to perform some business logic on that model and then set that model to parent binding. I hope the fiddle is explanatory.
I am also adding a button to the template which when clicked should invoke a submit function:
<button ng-click="submit()">click me</button>
It seems the button is working fine, but why is $scope.$watch() is not begin triggered? In a normal situation, if I change the view value it will automatically update the model value. But now it isn't.
$watch requires a dollar sign, and you pass either a function or a string that is evaluated on your scope, i.e.:
$scope.$watch('age', function(value) {
There are many more errors in your code, for instance you don't have a declared variable called 'age' so this line will reference window.age and give you an error because it is undefined, you need to say $scope.age I think:
age = age+10;
It just looks like your updated fiddle is a playground, hope these point you in the right direction. I'd recommend going through the egghead.io angular videos.

Angular data propagation through scope tree with controllers

I'm in the process of learning Angular, and have come across a bit of a problem. I'm trying to create a set of directives that represent different levels within a javascript object. The object contains a number of different properties that depend on the state of other parts of the model. For example, if one of the sub properties is in an error state, the parent is also. I have an extremely over-simplified example HERE. Any help would be greatly appreciated. Especially if someone could explain what's going wrong with the example and offer advise on high-level best practices for angular design. Thanks.
The problem with your example has to do with the new scope that is created by ng-repeat. I'll refer you here for a very detailed explanation, but here's the takeaway:
For each item/iteration, ng-repeat creates a new scope, which prototypically inherits from the parent scope, but it also assigns the item's value to a new property on the new child
scope.
If item is a primitive, essentially a copy of the value is assigned to the new child scope property. Changing the child scope property's value (i.e., using ng-model) does not change the array the parent scope references.
It's a confusing issue with a simple solution: Make your bindable values objects instead of primitives.
In your example, replace
scope.innerValues = [1,2,3];
with
scope.innerValues = [{value: 1}, {value:2}, {value:3}];
Here's your example modified to work: http://plnkr.co/edit/IXKk75721MHNsI0zeBEG?p=preview

Angular multiple instances of same directive, scope is not isolated

I have a problem when creating multiple directives with isolated scope: when I change something in 1st directive it also makes changes in all other directives.
Here is a working example: http://plnkr.co/edit/drBghqHHx2qz20fT91mi?p=preview
(try to add more of Type1 'Available notifications' - a change in 1st will reflect in all other directives of Type1)
I found some solutions to similar problems here but they don't work in my case. Also found a working solution with mapping 'subscription' data to local scope variables in directive (app.js, line 76) but I think there should be a more general way to do this right?
In your directive 'notificationitem' you have the following code, keep it in mind as i explan:
// if all variables are mapped in this way than works
//$scope.enabled = $scope.subscription.enabled;
The reason why all of the 'isolated' scopes are updating is because of this code in your scope declaration in the same directive (notificationitem):
scope: {
subscription: '=',
index: '#'
},
The equal sign on subscription is angular's way of saying "Whenever the current scope updates, go to the parent and update that value as well." This means whenever you update your 'isolated' scope, it updates the parent scope as well. Since all of these isolated scopes are binding to the parent, they will change as well.
Since you want the subscription.value to be the default value of that text field, you will need to do exactly what your commented code is doing:
scope.value = scope.subscription.value;
This will create an isolated value inside of the isolated scope. When scope.value changes, scope.subscription.value will not. All of the text fields now have their own 'value' to keep track of.
Check out this article for information on directive bindings: http://www.ng-newsletter.com/posts/directives.html
Also, another way to get the default value would be to inject your service into the directive, if you don't like the above solution. Hope this all helps.

Is 'this' truly a reference to "$scope" in AngularJS's context?

I'm quite new to the superheroic framework AngularJS, so please excuse my noobness! So, while I was trying to develop a simple SPA that allowed admins to change permissions of other users in my website through a table filled with selection checkboxes, I came by an issue. I defined $scope.checked so I could know how many users the administrator chose and also $scope.mainCheckBox which would hold the correct CSS class for the major checkbox (like the one from Gmail, which you use to select all/none) in each of the three situations: all users selected, no users selected and partial selection. This simple logic goes as follows:
$scope.updateMainCheckBox = function(){
if(this.checked==this.users.length) this.mainCheckBox = "qm-icon-checked";
else if(!this.checked) this.mainCheckBox = "";
else this.mainCheckBox = "qm-icon-minus";
};
Running this at the end of the ng-click event callback of the other checkboxes, this code was capable of choosing the class correctly but it wouldn't assign the class to the mainCheckBox. When I changed every this.mainCheckBoxto $scope.mainCheckBox it all went well and the page would behave as expected.
So, I know in vanilla Js this is a reference to the window object but since AngularJS has its own event handling loop, I believe that's not the case now. I expected it to be a ref to $scope, since checked and users were found, but apparently it's not. So, what's happening here? And also, is there a more "angular" way to implement this?
You're mostly right--in the case of your function, this did reference a scope. But, it did not reference $scope; instead, it referenced one of $scope's children.
Try this: add some console.log statements to log out the ID of the $scope variable, and then the ID of the this scope in your callback:
$scope.updateMainCheckBox = function(){
console.log('$scope:', $scope.$id);
console.log('this:', this.$id);
// ...
};
You'll find the IDs are different. This is because the ngRepeat directive creates a child scope, which is a scope that inherits prototypally from its parent. This means that when you assign directly to a value on that scope, it's not reaching the parent scope, which is $scope.
I highly recommend a read through the excellent (and lengthy) "The Nuances of Scope Prototypal Inheritance" by Mark Rajcok; it esplains why you see this seemingly strange behavior on child scopes. (You can also find a copy on the AngularJS wiki.)

Setting and getting bootstrap radio button inside angular repeat loop

I am trying to set the default button and get the current selected value.
The example without the repeat loop works.
Here is my plnkr:
http://plnkr.co/edit/t9CefA5bhLZs3RASmEUG?p=preview
It is based on this example:
http://plnkr.co/edit/LFj4inY9TLYZs9z7yHCr?p=preview
Thank you!
So, there were 2 things going on in your plunker.
Firstly the btn-radio attribute doesn't need interpolation ({{}}) you can (and should) provide an expression directly. So just write btn-radio="company.id" instead of btn-radio="{{company.id}}".
Secondly, you must know that the ng-repeat directive creates a new scope. This is a very, very common conceptual issue people have with AngularJS so I would encourage you to read https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
Coming back to you particular problem you can either change you ng-model expression to point to a parent scope (ng-model="$parent.radioModel") or bind to an object property (ng-model="radioModel.id").
Here is a working plunk with the second approach:
http://plnkr.co/edit/uGAxaRlPFK6sD4tRjGXX?p=preview

Categories