My code scenario:
I have Angular 2 app which has, out of multiple fields, one input field (for lookup), that opens a decade old URL in a popup window (using window.open()) to get some lookup data. And it calls back a function from the window object of the parent page.
The function is defined in index.html page of the Angular 2 application like this:
<script>
function handler(res) {
var value = res;
}
</script>
The need:
The variable value now has to be tied/bound to the input's model named inputModel. Is there way by which this can be achieved?
Please Note: I am aware that this is not a good practice of having <script> tags, everything has to be component based. But its how I have received the task :-P
Thank you!
You can store the value on the window object like this: window['value'] = res;
Now, you can access the value of res inside your angular components like this window['value']
But, the problem now is that this will only work if the handler has already been invoked by the time the angular component reads the value from the window object. Your angular component will not be notified of any subsequent handler calls which update the value property in the window object.
So, you need a mechanism for the global handler function to notify your angular component that the value property on your window object has been updated.
Here is one way of communicating with your angular component from outside angular:
Inside your angular component's template, create a hidden input
Attach a (click) handler to this.
In your global handler function, use document.getElementById('hidden input's id').click() to simulate a click.
Now, the (click) handler you created in step 2, which is inside angular's context will get invoked.
Now, every time the global handler function is invoked, the (click) handler within angular's context will be invoked, essentially telling angular to get the updated value from the window object.
Here is a working Plunker
In this plunker, I've attached the global handler function to the html's onclick handler which is outside angular's context to simulate your requirement. This click event will be communicated to the angular component using the approach I mentioned above.
Related
Basically I have created a function to update a scope variable
// controller.js
$scope.variable = 0;
$scope.$watch('variable', function(){
console.log("change from watch");
});
$scope.increment = function(){
$scope.variable++;
console.log($scope.variable)
}
When I bind a key to this function,
Mousetrap.bind('j', $scope.increment)
the console.log in the browser shows that the variable is incremented when the key, in this case "j", is being pressed, but the $scope.$watch function above is never called and the console.log message "change from watch" is not fired.
When I attach a click handler, however, on the html file,
// index.html
<a ng-click="increment()">{{ variable }}</a>
The variable increases, the console.log shows that the $scope.variable is incremented and the $watch function is being fired as well.
In addition to this problem, {{ variable }} does not change when I use the key binding, yet it changes when I click on it.
My guess is that there is something lacking in Mousetrap that causes the function to fire but not in sync with AngularJS $scope?
Make sure you read about Databinding in AngularJS. Basically, in order for AngularJS to react to changes, something needs to trigger a $digest. The built-in ngClick directive does that under the hood.
When using non-AngularJS APIs (such as Mousetrap), you need to manually wrap the callback in $apply, e.g.:
Mousetrap.bind('j', () => $scope.$apply($scope.increment));
I have a directive for parent scope, I also have another directive for child scope. In a template, I have several parent-child scope. Like this.
ParentScope1
- ChildScope1
ParentScope2
- ChildScope2
If I change a value in Parent, I will broadcast it to Child. I am using $rootScope.$broadcast to broadcast from parent. I am using $rootScope.$on to accept this change in child.
My problem is:
Now, If I change a value in ParentScope1, it will broadcast to ChildScope1. Then I will change a value in ParentScope2, it will broadcast to ChildScope2, but it will also broadcast to ChildScope1.
I want: Change a value in ParentScope1, it will broadcast to ChildScope1. Change a value in ParentScope2, it will broadcast to ChildScope2. I search online for some time but did not find the solution for it. Maybe I did not use the correct keywords for searching it. Please advise. Thank you.
In your definition of directive set
scope : true
then use
$scope.$broadcast
$scope.$on
this should probably works fine
post your code so we have a better view of the problem
You are looking for communication between parent and child directive, you can use below approach but both directive will be tightly coupled using this-
Require a controller - Get the handle of same node of parent node directive controller.
Require: '^parnetDireName' is used to find the controller on parent node. Without ^, it will find for same node only. '?' is used when controller may not be available.''?^", "?^^", "^^". Fourth parameter of link function is used to get the controller. It can use the controller prop/method. You can also access multiple controller because Require will have array - require: ['^dir1','^dir2']. Link function will have cntrl array and it can be access through array element in the same sequence
Pre link and post link function for nested directive -
Default link function is post link function.
Use keyword post to define it explicitly
Child post link function is executed first if parent and child both has Post link function
Parent link function is executed first if both parent and child has pre-link function.
Controller is executed before link function
---------------------------------Another decoupled way ---------------------
There are three ways to setup the relation between directive scope and containing controller scope-
- Directive with shared scope with containing controller. Any new
item/modified item by directive will be part of parent scope. It is
default or scope:false
- Directive with inherited scope. Any new item added by directive will not be visible by containing controller. Directive scope can read all data from parent scope. Use scope:true property to activate it. Child can see parent data and can override or create new variable.
- Isolated scope. Both scope cannot read each other data. Object mapping is required to read the parent data. Directive scope mapping will have object name and same object will be passed from html. There are three ways to receive the parameter-
- Complete object Data -> '=' is used
- Simple value like flag as a String- '#' is used . '#sampleVar', where sampleVar is the name of variable in the html.
Scope {
cntrollerStrVarName: '#htmlStrVarName'
}
- Function parameter - '&' is used to pass the parameter. Method parameter can be overridden using ({paramName: 'value'})
We interop our angularJS web components with a jqxGrid. When the user edits in a cell, we create a custom typeahead editor (written in angular). When the editor is destroyed, I noticed that my $watches array doesn't return back to the previous value.
I am creating a new isolateScope for my directive, which I then compile and then append to the DOM element that JQX passes to me when the editor is needed:
var scope = $rootScope.$new(true);
var customEditor = $compile(directive)(scope);
What do I have to do in order to clean up these $watches?
Its likely that the new scope you are creating via
var scope = $rootScope.$new(true);
Is not being destroyed by the jqxGrid once the jqxGrid is done with the editor.
To clean up the watches, you simply need to ensure that a call is made to
scope.$destroy();
The tricky part is figuring out when to execute the destroy call; I believe the jqxGrid should raise events such as beforeEdit and afterEdit which you can subscribe to; the place where the $destroy() call should be made is within an event handler for the afterEdit event.
Here is the way to clean up watchers effectively.
Should angular $watch be removed when scope destroyed?
Hope this helps.
I wanted to use Angular 1.5's component to get benefit from its one-way binding: <hero-detail hero="ctrl.hero" save="ctrl.save(hero)"></hero-detail>. But, as Todd Motto points on his blog: Todd Motto's blog: One-way data-binding in Angular 1.5, it works properly only for primitives. So I had to bind primitives:
<hero-detail
name="ctrl.hero.name"
id="ctrl.hero.id"
save="ctrl.save(ctrl.hero)"
></hero-detail>
And next in component, on $onInit hook, make hero object from primitives:
HeroDetailController.prototype.$onInit = function(){
this.hero = {
name: this.name,
id: this.id
}
console.log('OnInit called');
}
And call specified function when user clicks save. Weird part is, that if user changes hero's name inside component and clicks save, when function bound from parent is called, it does not have changes from hero-detail component. I made a plunker which shows my problem: Plunk which shows problem with children/parent output binding in Angular 1.5 - if you open Developer Console, click "Set name..." and then click save, you will see console.logs which will show you that from hero-detail it is Spawn2, but in parent context (where should be logic, like talking to $http service), it has still old value Spawn. Am I missing something?
Code from Angular docs looks pretty like my code:
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
I have no clue what's going on. Thank you in advance for helping me to deal with this problem.
P.S. I had some problem with Plunk versions, now everything is OK - in Developer Console in your browser you can see problems with updates
To avoid confusion about the scope of variables (parent or child), prefix injected variables with $.
/* WAS
<hero-detail
name="ctrl.hero.name"
id="ctrl.hero.id"
save="ctrl.save(ctrl.hero)"
></hero-detail>
*/
//SHOULD BE
<hero-detail
name="ctrl.hero.name"
id="ctrl.hero.id"
save="ctrl.save($hero)"
></hero-detail>
Then in your code:
HeroDetailController.prototype.saveHero = function(hero) {
console.log('Hero name from child: ' + hero.name);
this.save({
$hero: hero
});
};
This way you can tell which variables of the expression are from the parent scope and which variables are from the directives scope.
What's the best way to load a ko component with JavaScript code instead of defining a custom element in html? I tried with ko.components.defaultLoader.load but my component constructor does not hit.
I double checked and the component appears to be registered.
I believe what you are looking for is function ko.components.get(componentName, callback). What this method does is ask the component loaders to resolve the component name until it finds one. If it doesn't find one, it will call callback(null). If it does fine one, it will call callback(componentDefinition), where componentDefinition is the object used to register the component, like { viewmodel: ..., template: ...}.
As far as I can tell, there isn't a ready made function which returns a "working" component. What you have to do after getting the componentDefinition object is something like:
convert the template into a DOM element
instantiate the viewmodel (if defined)
bind the viewmodel to the DOM element
Note that this is not straight away because templates and view models can be defined in several ways.
I recommend looking at https://github.com/knockout/knockout/blob/master/src/components/componentBinding.js and see how it's done here (from line 38).
I hope this works for you, otherwise you could consider other options, like dynamically creating a div element in code with a component binding where the component name and parameters are bound to properties of a view model. Then bind this view model to the div element you just created. This should work "code only" which much less code than the other route.