Angular, Loop through already created components on the DOM - javascript

I'm pretty new to angular, but I have managed to get most of the concepts down. However, I can't seem to figure out how I should go about solving my issue.
I have one component, that has other components as its children. I want to be able to loop through the child components, and add data to them separately, either before they are rendered, or after they are rendered.
The master component is an HTML version of a government form, and I have scattered throughout it a simple component that is supposed to display text from a database relevant to the field it is in.
If I was using something like jQuery, I would just attach an id to each component, and loop through them, adding data if the component id matches the key of the JSON object.
Is there any standard way to do something like this with angular?

You'd be better off "binding" any information you'd like to provide to your child components instead of implementing a vanilla/jQuery solution that involves querying the DOM and then manually manipulating the DOM.
Take a look at property-binding and two-way binding.
Here's a quick example:
<my-child-component [someProperty]="myValue"></my-child-component>
<my-menu-component [menu]="menuData"></my-menu-component>

You probably should rethink the way you are trying to do it, when you start with Angular you need to leave a lot of concept that you learnt in jQuery behind.
We don't manipulate the DOM directly in Angular, we manipulate the data that is bound to the template and that binding is what updates the DOM.
Tom added an answer as I was typing, he is saying what I was going to.

The angular way to achieve what you want is to use one-way data binding. Check out the example in the documentation.
In the example they have a child component in the template html of the app.
<bank-account bankName="RBC" account-id="4747"></bank-account>
You can one-way bind data by referencing a public property on the parent and using the square bracket syntax to indicate one-way binding like so:
<bank-account [bankName]="somePropertyOnTheParent" [account-id]="someOtherPropertyOnTheParent"></bank-account>

Related

Should I use raw js or jquery to target DOM elements in a Vuejs 3 app?

 I'm creating an app using Nodejs and Vuejs 3. In this app I have made a sidebar that gets all links from a routes file and present them. This sidebar consists in a component that parents a list of other recursive link components.
 Since the links are recursive and many, I find it hard to deal with class toggling (active, showing, collapsed, etc.) on each of them and relate them to one another (if one is active the others shouldn't be) using only Vue. Should I use querySelector or any frameworks such as JQuery to handle them or should I try to stick with a pure Vuejs approach?
Edit:
 I don't want to gather the community's opinion on it. My aim is to understand pragmatically why I should or shouldn't manipulate the DOM "outside" of Vue.
If you're using Vue then let it be in control of the DOM; mucking around directly will only create conflicts and woe.
(The same applies to other SPA frameworks such as React and Angular.)
The main reason not to touch the DOM is that Vue works by modifying the DOM on its own, and expects to have complete control over it: when rendering components the framework is removing old DOM elements, adding new ones, updating event bindings, etc; and a lot of it is optimized to only update the DOM nodes that need to be updated.
If you go in there and start making direct changes that Vue doesn't know about, then it's likely that your own changes will get overwritten by Vue the next time it needs to render, or that your changes will overwrite bindings that Vue is depending on.
If you're very knowledgeable about Vue's lifecycle and know how to control when it does and does not render, it is possible to work with both together -- but even then it's still not a great idea. Vue and jQuery do very similar things, but in utterly different ways. In jQuery you build up the page and then use DOM traversals and event handlers to modify it; everything lives inside the DOM. In Vue you build up a bunch of components that manage their own state and rendering; the DOM is basically a side effect of the application state.
By trying to use both together you lose most of the advantages of each of them in isolation, and introduce a lot of complexity in having to manage two competing theories of state and render management (not to mention dealing with communicating data between them). Every time I've contemplated embedding a jQuery widget inside a Vue app, it's turned out to be much easier to just rewrite the widget in Vue directly.
This does mean changing a lot of habits about working with the DOM that you may have built up from past jQuery work. It sounds like you're trying to draw the whole DOM and then build your control structure into it afterwards, which is a natural way to think if you're used to jQuery; in Vue you'll want to build all of that logic into components so the framework can do the work for you. I'd suggest making one Vue component for a link that manages its own state for open / closed / active etc, that recurses to its children only when "open". Then just call that once with the top of your nav data instead of trying to manage the whole tree directly after the fact as you would in jQuery.

How to dynamically access children of a slot

Let's say I have a SearchForm Component that has a Reset button, as well as a slot to include any desired SearchField Components. When I click SearchForm's Reset button, I'd like to call each SearchField's reset method, but I'm having a hard time understanding how to do this dynamically... I obviously don't want to add refs to each SearchField because these aren't static and can change when using the SearchForm in some other part of the application. Fiddle for example.
In Vue2, it seemed liked there was some sort of $children property, but that was taken out in Vue3. I was thinking I could potentially use querySelectorAll to access all "input" elements, but I didn't see how I could access the DOM element's component instance (similar to jQuery's $ selector). If I access the $slots.default() and loop over it, I get some weird object that isn't a component instance... or rather, it doesn't have the typical properties that the component instance has, and I have no clue how to access the actual instance from here.
It's also possible I'm not thinking in a Vue-centric way, as I'm new to the framework, so how can I solve this issue?
I've come up with this solution, but I don't like it, as it adds some minor coupling. Basically, I listen for when the field is created, check its parent, and if it's a form, I push it onto the array of children. Then when the parent's reset is called, it loops through its children. This is a fragile approach because it requires the direct parent to be the form... if the field was nested inside of another component, it won't be added to the form's fields. I'm also pretty sure this breaks the best practices of the framework. It's a shame there doesn't appear to be a way of accessing child instances (without being forced to use ref)... that seems like it'd be desired by a lot of devs.
I came up with yet another way, but once again, seems a little shady because I'm accessing the DOM element's private property __vueParentComponent. I like it better than the previous answer because it's not coupled, and I can use getElementsByTagName. Fiddle for reference. This is the relevant code that I added as a method in SearchForm:
getFields() {
const fields = this.$el.getElementsByTagName("input");
// getElementsByTagName returns an HTMLCollection, which doesn't have map,
// so let's use spread to make an array and use map
return [...fields].map((fieldEl) => {
return fieldEl.__vueParentComponent.proxy;
});
}
Last solution... this one seems to be more stable and the proper Vue way. You use provide/inject; the parent provides the value, and the child injects it, so it can use it. Vuetify does something similar, but they have their own register and unregister methods, which I've created in the Fiddle but as a rudimentary implementation. The only caveat being if you're using TypeScript, the inject won't work properly, and you'll have to use one of these solutions.

Does React (hooks) require the use of states in order to do its virtual dom diff magic?

This always puzzled me. If I said "Thank you React, your state hooks are awesome, but I'm just gonna do my direct dom manipulation here", would react still do the virtual dom comparison in order to update only that specific item? would I still benefit from the virtual dom 'situation'?
Is there any difference between using react to directly manipulate dom without states, and using a standard HTML file with imported vanilla js code?
For clarity, here's an example,
Let's say I have function printHellol() triggered by a button 'click' in my JSX. The function targets the ID of an element and changes the text content.
I couldn't find the answer anywhere. Thanks!
Anything that his held in state becomes part of an object that react renders as a detached element from the dom. It's in essence creating a separate environment for all states. Anything that is used outside of state can be considered to be part of the direct dom object, causing the page to be rerendered when updates occur. In other words you would need to use the react specific state if you want to access the virtual dom specific environment.

Manipulating data on parent with $event or via $parent in Vue.js

I'm fairly new to Vue.js and I ended up manipulating an array on a parent component using $parent. I'm just wondering if there is some danger in doing it like this:
setTitle(title) {
this.$parent.items[this.index].name = title;
this.editTitle = false;
}
Instead of emitting an event with $emit, and then listening for that on the parent?
Will this burn me in any way in the future?
I' wondering because I've never seen it showed like the first solution in any of the tutorials I've come across.
There is some danger in manipulating a parent through a child, similar to manipulating a child through a parent. The main issue tends to come in separation of responsibility and coupling, if you reuse the code you need to remove or modify any code that manipulates another object in the system.
By emitting an event and letting the parent handle the updated information you can reuse the object in multiple places without modification as any parent that doesn't need to update their items can just ignore the event.
It also makes it harder to maintain the code as someone might see the array get updated but not see the code in the parent object that updates it. In your example it would not be hard to find but if your program became more complex it could take time to find and update or debug.

How can I handle a Knockout.js mapper bastard-child?

I'm using the mapper plugin on Knockout.js to handle JSON coming from a server. I've set up a parent and a child, however some children don't have parents. These stray children need a handler to put them in a new parent or model like "foster parent" and have a way to access the foster parent and iterate with a "foreach" in a separate list.
Here's a code sample of what I have so far: http://pastie.org/3708368
Where would the handler go for foster parenting? Or can I just prepend the parents with a special foster parent?
The basic use case is the children are unassigned tasks (to give context).
Thanks for the input!
I was able to draw a lot on this advanced example: http://jsfiddle.net/rniemeyer/UdXr4/
It simulates a seating chart and uses the author's custom plugin, Knockout-sortable. I highly recommend it. After examining the example, I realized there was no need to use a mapper but instead I could define a view that accessed a specific collection.
Props to the author, that's a lot of helpful and useful work.
Update
There's a much better technique to set properties of children using the create during mapping. See this GitHub issue: https://github.com/rniemeyer/knockout-sortable/issues/15

Categories