Angular Directive not working with *ngIf. Calling directive from component - javascript

I have two DIVs - one, that is always visible, and another one, that gets displayed after I click a button (it is toggable):
<div>
<div>
<small>ADDRESS</small>
<p [appQueryHighlight]="query">{{ address}}</p>
</div>
</div>
<div *ngIf="showDetails">
<div>
<small>NAME</small>
<p [appQueryHighlight]="query">{{ name}}</p>
</div>
</div>
My custom directive works well on the first DIV (it changes the color of letters inside the paragraph that match the query entered in an "input"), but does not work on the second DIV.
Here is an example of how "Test address" looks like when query is "addr":
Test addresswhere bold text is for example red-colored text.
But when I have name John, and query is "Joh", it should also be colored once shown with the button, but it is not.
As I understand, I need to re-run the directive after I click and toggle the second div. Also, probably I have to do this with delay, so it has time to be shown. How can I do this?

UPDATE
Problem with my directive is related to *ngFor - both DIV are located withing ngFor. I have to call directive ngOnChanges or ngOnInit only after the new list get propagated based on the query. I have no idea how to do this, and currently, directives get loaded before ngFor fully loads - this cause problems.
Here is my previous answer:
I finally managed to solve this problem, thanks to #iHazCode.
So firstly, this is probably a duplication of problem described here: Angular 4 call directive method from component
Because my directive hightlights the specific letters in paragraph based on input query, each time the query changes, the directive should fire, thus I am using ngOnChanges withing the directive.
This is not working with *ngIf, because when ngOnChanges fires, the second div and paragraph is not visible.
As a workaround, we can access the directive from out component using #ViewChildren:
#ViewChildren(QueryHighlightDirective) dirs;
And then call it's ngOnChanges method, once div is toggled. In my case, the problem was related with last occurence of the directive within the component:
toggleDetails() {
...
this.dirs.last.ngOnChanges();
}
That was exactly what I was looking for, I hope it will help someone.
In my case I also encountered another problem - the defined DIVs are within *ngFor, which is also propagated based on the query, so I have to make sure that the list will get propagated before calling ngOnChanges. I haven't find a solution for this yet.

ngIf does not show/hide elements. ngIf determines if the element exists on the DOM. You can verify this by looking at the source of your rendered page. Change the code to use ngHide/Show instead.

Related

How Do I Target A Specific Node In React Component and Have Independent Behavior When Component is Reused?

I am trying to target a specific node in my react reusable component and perform an operation on it. But I want to be able to do same thing several times independently because I will be returning this my reusable component several times on the same page on my App.js. How do I target that div node uniquely?
Here is my situation:
I created a react class component (I am not using function component for this project). This component contains two divs and a button. The first div is a red box. Under it is the second div which is empty with the class "result". There is also a button. When I click this button, the package Html2Canvas will convert the first div into an image and append it to the second div.
Now from the Html2Canvas documentation, they only specified how to append the generated canvas to the body of the DOM:
html2canvas(document.querySelector("#capture")).then(canvas => {
document.body.appendChild(canvas)
});
Since I needed to append to that empty second div, I did:
html2canvas(document.querySelector("#capture")).then(canvas => {
document.queryselector('.result').appendChild(canvas)
});
This would have been fine if I am just using this component once on a page, but in my App.js, I am actually returning this my component multiple times, so what now happens is that each time I click that button, it targets only the first instance of the empty whereas what I need is that each occurrence of my component should behave like the first one independently.
Yes I have tried using CreateRef() to target the current occurrence oof the empty dive but it does not work. It sees the ref as undefined when I call it inside the Html2Caanvas function. But outside of it, it is defined. Ok I tried creating the ref inside, still gave an error of undefined.
I understand that this whole thing I have written may be somewhat vague, so here is a CodeSandBox example of what I want.
When you open it and click on the first "Finish" button, you will see that the red box gets converted to an image, and the image is added under the red box, that is, it is appended to the div with class "result". But when you click on the second "Finish" button, instead of the generated image of the blue box coming under the blue box div, it goes under or beside the previous image. The expected behavior is for it to appear under the blue box. And if I replicate that component "ReUsable" as many times as I want in the App.js, the same behavior is expected. Independence.
I know this is a lot. I look forward to and appreciate your solutions.
First, the ref approach was great (the one you commented out), only that you were referencing the containerRef and not the screenshotRef.
the main issue came from the callback function passed to html2canvas, even though you called bind on handleFinish, binding "this" context to the current class context, the execution context of "this" in the callback is undefined because you used a regular function and regular function always refers to the context of the function being called(html2canvas).
arrow functions on the other hand treat the "this" keyword differently. They don't define their own context.
kindly update your handleFinish method to this
handleFinish(event) {
html2canvas(this.containerRef.current, 1).then((canvas) =>{
this.screenshotRef.current.append(canvas);
// document.querySelector(".result").append(canvas);
});
}

Vue.js: Communicate with components that are down the DOM

I'm just trying out Vue.js and I'm struggling with the key concept of passing information up and down the DOM from one component to the other.
Consider this example in which a container-toggle button should toggle all components within the container, or say, set them all to "true" or "false".
<div id="app">
<p>
<strong>Welcome!</strong>
Click the "true/false" buttons to toggle them.
Click the "Toggle all" button to toggle them all!
</p>
<app-toggle-container>
<app-toggle></app-toggle>
<app-toggle></app-toggle>
</app-toggle-container>
<app-toggle-container>
<app-toggle></app-toggle>
<app-toggle></app-toggle>
<app-toggle></app-toggle>
</app-toggle-container>
</div>
In this code pen, I've defined app-toggle and app-toggle-container as components: https://codepen.io/fiedl/pen/mmqLMN?editors=1010
But I can't find a good way to pass the information down from the container to the separate toggles.
Also, in a second step, when trying the other way round, for example, to have the "Toggle all" button just show "true" if all toggles are true, or to show "false" when all toggles are false, I can't find a way to pass the information of the current state of the toggles up to the container.
This doesn't seem like an uncommon problem. What is the proper way to do this in Vue.js? Or am I thinking about this it in the wrong way?
Quickly, I've found $broadcast and $dispatch. But as they are dropped in Vue.js 2, I'm most probably thinking about it in the wrong way :)
I forked your pen http://codepen.io/nicooga/pen/wdPXvJ.
Turns out theres a $children property for Vue components that contains your children components [controllers]. You can iterate over them and do stuff with them.
this.$children.forEach(c => c.toggle());
See
https://v2.vuejs.org/v2/api/#vm-children
VueJs Calling method in Child components.

How to hide/show adf component automatically

Hi I want to hide an adf component automatically.I have a selectOneChoice that contain some number (from 1 to 12).
Example when I select 2, it show's two field automatically without clicking any button..
i used this function to hide a declared componenet but just when i click a button..
function enableField(actionEvent) {
var nameInputText = actionEvent.getSource().findComponent("soc1");
nameInputText.setProperty("visible", true);
actionEvent.cancel();
}
i set the componement "soc1" visible = true than through the javascript function i change it..
SO the probleme here is how to read the number from the selectonechoise and how to set the component visible directly without clicking any button.
Actually Rendered won't do what you want. You want to use Visible property instead. Rendered causes the actual markup for the component not to be rendered on the page, so a Partial Refresh will not cause it to appear. Rendered is reserved, usually, to hide items that are secure. We set rendered property to false on the item(s), but then refresh the parent containing component - usually a layout manager - then it works. So either refresh the layout manager that contains the item or use Visible. I demonstrated this exact use case last week in class and it works as described.
Basicaly, you don't need javascript solution or any programming to achieve this.
You should set attributes rendered(this way component won't be rendered at the page at all) and partialTriggers that points to selectOneChoice component for components you want to show or hide. Also you have to set autoSubmit="true" for your selectOneChoice component.
<af:selectOneChoice id="soc1" autoSubmit="true" .../>
<af:panelGroupLayout id="plg1" partialTriggers="soc1">
<af:outputText id="ot1" rendered="#{bindings.lov.inputValue le 1}" inputValue="text1"/>
</af:panelGroupLayout>
Note: its not working code, just a sample
It will work following way, on valueChange event at selectOneChoice component value is submitted and partialRefresh fires for components that have it in partialTriggers specified. And rendered attribute will either render or remove component depending on it's EL expression. Components that should be managed wrapped into another component to make sure PPR reach it when they ain't rendered.

How to programmatically add components or views inside other components/views in ember js

I have a component say {{component-1}} which gets called many times and creates a custom-texbox container and label as many times as it gets called.
Now whenever a user writes something in it a suggestion box should appear below it. Since the suggestion box can be reused everywhere i dont want to have a separate suggestion box for each {{component-1}}, rather i want to have another component called {{suggestion-box}} that gets inserted inside component-1 i.e. the textbox container.
I dont want {{suggestion-box}} to be inside dom at all since it is needed only when somebody types in it. I want to add/insert it into {{component-1}} when someone types. Instead of a component i even tried to use a view
Here are the different things i have tried and failed
Note:
suggestionBox is the component
textbox-container is an element inside {{component-1}}
Inside {{component-1}}this.$().find(".textbox-container").append(this.suggestionBox );where this.suggestionBox = suggestionBoxComponent.create(); I have event tried suggestionBoxView.create();It gives me the error that i one view cant be inserted into another and i need to use containerView
var tmp = Ember.Handlebars.compile('<div class=".suggestionBox"></div>');this.$().find('.textbox-container').append(tmp());I get the error called
Uncaught TypeError: Cannot read property 'push' of undefined
I even tried to use view instead of component i.e. make suggestionBox a view but then again i cannot insert one view inside another
I have tried a lot more things.
Few points:
I dont want alternate solutions of how textbox and suggestion box could be created
How to pass information from a component or a a template to a view? Say i do {{view "suggestion-box"}} inside component-1 template, how do i pass values to it? Say for components we pass in the context like this {{component1 sampleVar1=val1 sampleVar2=val2}}
i Want to know how to programmatically add a component or a view and if it is a view how to pass the data to it?
I dont want to use container-view since it will cause more complexities, however if your solution allows me to pass value from {{component-1}} to container-view and inturn pass it to corresponding childView1 and childView2 then that solution is acceptable
Just an update:
I even tried to use a view container inside the {{component-1}}
I also tried to use view block inside {{component-1}} i.e.
{{#view "view-name"}}
----earlier component elements here-----
{{/view}}
In both the above points "view-name" is a ContainerView which is getting inserted properly but the component element are not getting inserted
This can be achieved with a ContainerView and the viewName attribute. The component or view can access the ContainerView or any other view that is part of a template through its assigned viewName.
Example,
hbs
{{view Ember.ContainerView viewName="my-menu-container"}}
js - then from the component or view
this.get("my-menu-container").pushObject(suggestionBoxViewOrComponent);
http://emberjs.jsbin.com/yoyujeqi/1/edit
p.s. the context of the example is not relevant to the suggestion box, sorry about that, as it has been extracted from another answer related to adding a menu view dynamically.

What's the explanation behind this (seemingly) unexpected behavior from ng-class.

Here's a plunker I created: http://plnkr.co/edit/ZoKsO7wu5OvCYtwEi9Iy?p=preview.
Click on one of the items rendered using ng-repeat in the list, for e.g a.
Thereafter click on clear selection which clears the selection using jQuery, outside of AngularJS.
Now once again click the same item whose selection was just cleared, in this case a
It's seen that a cannot be selected now. However, the other items(b, c) in the list can be selected. Once another item has been selected and it's selection cleared, a can be selected again!
Why is ng-class behaving unexpectedly here?
I wouldn't use jQuery for that but if you still want then you must let angular know of any modifications you do.
You may think that you cleared the selection, but what the jQuery callback only does is to remove the class. from angular's perspective it is still selected , so it doesn't fire ngClass watchExpression to add the class again.
Use $scope.$apply():
$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
Here is a plunker: http://plnkr.co/edit/C6jcmfU8V4tab8oeDJES?p=preview
I moved your click event listener into the controller:
$('#removeSelection').click(function(){
$scope.$apply(function(){
$scope.selectedId = null;
})
});
Much better to just use angular built-in ngClick:
<a ng-click="selectedId = null">Click to Clear Selection</a>

Categories