How do you force Ember to rerender a component? - javascript

I have an Ember component that uses jQuery to add a canvas chart. When I change routes, I get a new model, but Ember's automatic rerendering does not work in this case. In fact, I don't know how to make the component code which adds the chart re-run at all. How can I do this?
Would it work better if it was a view?

Without looking at code, I'm going to guess based on your statement of the model changing.
Component Currently
uiSetup: function(){
// do magic here...
}.on('didInsertElement')
Component with observes
Assuming the model in the component is named model, this would fire every time the model changed as well as when the element was initially inserted into the page. You could also break it into two separate functions if you need it to act differently on model change vs element inserted first time.
uiSetup: function(){
// do magic here...
}.on('didInsertElement').observes('model')

Related

Do Angular template functions get called whenever the UI updates?

I wrote this demo, and I want to make sure I understand the dynamics correctly.
In it whenever one of the checkboxes is selected it causes the corresponding merge of the observable checkbox events to trigger.
The trigger causes the redefinition of an array of instances that say whether a material table columns should be hidden or not.
The component also has this method that defines the material table columns:
getDisplayedColumns():string[] {
return this.columnDefinitions.filter(cd=>!cd.hide).map(cd=>cd.def);
}
IIUC the reason that method gets called is that it gets called any time the UI is updated. In other words Angular Change detection triggers it?
Your method is called from the template for every change detection cycle. The change detection mechanism tries to find any changes in the model of the component and then re-render changed parts.
But you can manage how the change detection mechanism works with your component by setting the change detection strategy.
Change detection strategies
By default change detection is launched for many reasons - clicks, scrolling, or other async events. Hence the method can be invoked for hundreds/thousands of times.
But you can use changeDetection: ChangeDetectionStrategy.OnPush in your component. The change detection mechanism checks changes only if #Input() properties have changed.
But it is only half of the solution...
Avoid using methods in the template
Another half - is not to use a method in the template at all and use class property there. Btw follow the same rule for getters as well
<mat-row *matRowDef="let row; columns: columns"></mat-row>
Everything you need is to set this.columns in your class when it should be really changed.
I hope it helps.
Yes. Every click or another event in the UI tries to call getDisplayedColumns once and again because it is a method in the html. You have to avoid that, setting a class property with this columns when it has loaded.
Look my corrections:
https://stackblitz.com/edit/angular-material-table-hide-columns-owzmtl?file=src/app/app.component.ts

Make Vue interpret components that have been inserted programmatically

I'd like to modify a div inside my Vue component's template using the Rangy library. I have code like this in one of my methods:
let tooltip = document.createElement('button');
tooltip.setAttribute('type', 'button');
tooltip.setAttribute('v-tooltip.top-center', '"msg"');
tooltipSpan.innerHTML = 'x';
// Insert span with tooltip after of mistake
range.collapse(false);
range.insertNode(tooltipSpan);
As you can see in the third line, I also want to use a v-tooltip component. If I could hardcode this inside the template, it'd simply be <button v-tooltip.top-center="msg">x</button>. But in my app, this component could be programmatically placed anywhere inside a div, so I need to insert it with JavaScript. There might also be more than one instance of that component.
Unfortunately, my approach does not work at all. It doesn't matter whether I use the approach mentioned above using the v-tooltip directive or if I simply insert a component (e.g., let tooltip = document.createElement('my-custom-tooltip');): The new DOM element is correctly inserted, but Vue does not recognize/interpret it as a component. In the above example, all I get is a simple, unstyled button element.
I tried this.$forceUpdate(), this.$nextTick() and a bunch of older methods (that have probably vanished from Vue 2), but nothing worked.
Is there a way to make Vue re-render after manual changes to he DOM, correctly interpreting components that have been inserted?
Vue "owns" the DOM tree belonging to any component; if you mess around in the DOM like you are, any Vue-specific things that you create or modify (like directives) won't magically work.
The DOM that is generated by Vue is a function of the component's data; any time the data changes Vue will re-render the DOM to reflect that change. Vue doesn't know how to deal with manual changes you make to the DOM.
I don't know specifically what you are trying to do and why Rangy is needed, but the correct way to do something like this would be to modify your data not the DOM, and then your template (or render function) takes care of rendering the new data.
Is there a way to make Vue re-render after manual changes to he DOM, correctly interpreting components that have been inserted?
No.

How to force view update in middle of code execution?

Every once in a while I have a situation where my code changes some variable binded to the view, or even change some attribute of a Dom element, but the view renders only when all the code is executed, when I usually I don't need the effect anymore, like for example an animation for an Ajax data load.
Is there a way or a command that forces the DOM or Angular to revalidate its bindings or simple refreshes the view?
If I understand your problem correctly it looks like you need to run change detection manually.
ChangeDetectorRef.detectChanges() would trigger change detection for view and child components.
ChangeDetection Docs

equivalent to ngAfterViewInit but general for all views

When views DOM is heavy, sometimes it gets some time to render it, especially on older mobile devices. I would like to put a spinner whenever the view is not rendered yet.
I can achieve that using ngAfterViewInit hook but doing it for every view provides a lot of duplicated code.
I was wondering if there is a global hook that is fired whenever rendering of current view is done.
As Jota mentioned in a comment, what you're asking for doesn't exist in angular. Something you could do, which may or may not be appropriate to your situation, is add a single spinner component to the root of your app (say, in app.component.ts) and create a service which can turn it on or off. This way, in each of your child components you could turn the spinner on in ngOnInit and turn it off in ngAfterViewInit.
Another option, if you're using the Angular Router, is to have this spinner component listen for router events: turning on at NavigationStart and turning off at NavigationEnd.

Ember component leaking state

I'm new to Ember and have a leaking state problem. I have a carousel widget that displays one item at a time and allows the user to click previous/next to see each item.
Here's the simplified carousel's component:
<button {{action "nextItem"}}>Next</button>
{{carousel-item item=selectedItem}}
Clicking next changes the selectedItem property so the next item is shown.
What I've realized is that the carousel-item component isn't re-initialized every time I move to a previous/next item. The DOM is reused each time, and the component's properties are shared since it's all one instance, which means I can have leaking state.
The alternative I see is to render all the items initially, so each has its own instance:
{{#each items as |item|}}
{{carousel-item item=item}}
{{/each}}
and to hide all but the selected item using CSS. However, this option kind of feels like a jQuery hack -- seems like Ember would have a better way. And I'm only ever showing one item at a time, so I hate to have so many extra DOM nodes when I don't need them.
What is the recommended way to handle this kind of a UI, where you only need one item shown at a time but don't want to share state between items? I'd imagine I should have one instance of the carousel-item component per item, instead of sharing an instance across all of them. But it doesn't feel right to instantiate every single carousel-item at first either. And I can't imagine the Ember way is to worry too much about the DOM details myself (determining which one is shown/hidden based on a class and some CSS).
Firstly, whatever framework or library you are using, jQuery, ember, angular, react, they are just a pack of JS/HTML/CSS right? So you should think in it's way, there is no magic!
So of course 1 component will only create 1 instance. If you just changed it's property(item in your demo), it just changed the property of an instance, other properties of it will remain as it is and triggered re-render. You cannot expect more. You have to manually reset other properties.
And yes, rendering everything by {{each}} looks stupid, but think about it, how could you create a smooth carousel animation by render only one DOM? At least you need to render 3 (current, previous and next) right?
Since carousel is a common UI, I recommend you to check existing ember addons fist before you write by yourself: https://emberobserver.com/?query=carousel
If I understood your problem correctly, the willUpdate hook in Ember.Component class should help you out. I this hook you can clear up the attributes, remove DOM objects, or anything at all. This will be called each time the component is about to re-render itself.
A simple example is of form:
willUpdate() {
Ember.$(this.get('element')).empty();
},
This will clear the DOM on each re-render forcing it to redraw elements.
You can try out other hooks too and see which event will serve your need. All of them are very helpful and serve different purpose.

Categories

Resources