How to handle style classes in Vue js? - javascript

I need add class dynamically and remove when it need it
I'm using texfield component from google material. When text input does not pass validation, error comes up , input borders should be red and a warning message below it. When everything is ok, green check icon. Picture below how it supposed to be.
All text goes thru regex validation and stores in error array (data->textfields-> errors) Thats how I track it if there is an error.
Here I'm dynamically adding styles when there is an error.The problem is when i'm clearing input (empty) 'tf-error' class still there with red borders, but I need to remove it, like it was before adding class 'tf-error'. What should I do?
gmcTextfield(
v-for='(item, idx) in textfields'
:key='idx'
:inputId='"textfield-"+idx'
label='item.label'
:className=`['fs', [textfields[idx].errors ? 'tf-error' : '']]`
:reference='item'
refProp='value'
:numOfErrorMsgs='item.numOfErrorMsgs'
)

I use in Vue the class binding to apply dynamic classes. You can see the doc here.
You define a binding for class and use an object as value, whre the key is the classes and the value is a boolean expression.
As example
<ul>
<li v-for="item in menuItems"
v-on:click="selected = item"
v-bind:class="{ selected: selected == item}">
{{item}}
</li>
</ul>

Related

How to two-way bind a checkbox using Angular?

I currently have an accordion with a bunch of options in the form of checkboxes. The user can select the checkboxes as expected however, I want to already have some of those checkboxes checked depending on certain conditions. The issue is sometimes that is determined after the page has loaded. Below are simplified examples to demonstrate my code (my code is for work and would not be able to share due to confidentiality issues).
My HTML looks like this:
<div class="row">
<div "ngFor="let value of filteredPeople" >
<div>
<input type="checkbox" (click)="selectPeople(value)" [checked]="checkedPeople.get(value)">
{{ value }}
</div>
</div>
</div
My Javascript:
public checkPeople() {
this.selectedPeople.forEach(element => {
this.checkedPeople.set(element, true);
});
}
To explain some variables and methods:
Variables:
filterPeople - a string array of all possible people
checkedPeople - a map with a KVP of string (the people) and boolean (whether or not their checkbox is checked)
selectedPeople - a string array of people whose checkboxes I want already checked
Methods:
selectPeople - checks the corresponding checkbox when user clicks on it
checkPeople - a method called when I want the specific checkboxes checked (these specific checkboxes change based on other factors so I cannot hard code it)
For some reason my checkPeople method does not seem to select the expected checkboxes, I am not sure why but I have a feeling it is to do with the fact that I have used "[checked]" in the HTML and that it should be something else. Can someone clarify the issue for me and help me identify a fix? (Note: as the title suggests, I am using Angular)
Edit:
I have just debugged my code and added breakpoints, the checkedPeople map has the correct mapping of people to true for each of the elements in selectedPeople which shows that the checkPeople method is working as expected. Therefore the issue must be with the [checked] attribute if I'm not mistaken. I cannot see why it wouldn't work though.
You should use [ngModel]="checkedPeople.get(value)"
instead of [checked]="checkedPeople.get(value)"
to initialize the checkbox and
(change)="checkUncheckPeople(value, $event)"
to update the value when you check or uncheck it, where
checkUncheckPeople(value, e) {
this.checkedPeople.set(value, e.target.value);
}
So, in conclusion, your HTML input element will be:
<input
type="checkbox"
[ngModel]="checkedPeople.get(value)"
(change)="checkUncheckPeople(value, $event)"
/>
If you choose to use an object instead of a map then you can also directly use
[(ngModel)]="checkedPeople[value]"
to two-way bind the variable

How to give non changing unique keys to list items in React?

I am trying to make an Input Component which Highlights the newly changed character. To make it happen I made the input element transparent and created a div absolutely position under it with same font size. Inside that div I render a list of span's with each character of the input string. to span I have applied an animation for 3s to change its color from red (highlighted) to black (normal).
Now to make list behave correctly I need to pass a key to each list item to uniquely identify each list item so that only the latest entered character gets highlighted and rest remains the same.
What I have tried
Use Index as keys.
works fine as long as I insert text in the end, but when any text is inserted in between the index of all following elements gets changed and therefore they all gets updated and therefore highlighted
Use character as key.
as we have only 26 characters as soon as any character repeats a have same keys for two different list items and again it behave strange
Use character and context around as key (i.e, ch + arr[index+1] + arr[index-1] etc..)
but again when it comes to similar patterns such as "aaaaa" etc.. it behaves unexpected.
Use random numbers / id generators (uuid etc.) / new Date().getTime().
but as soon as elements updates render function gets called and all list items recieve brand new keys so they all update
Use a counter in the parent component to count the total number of changes in the input field and assign that number as key to the newly added character component.
I don't know how to Implement that because for that I need to check something like if the element doesn't already have a key assign the new number else keep the the old one.
Is there any way to give new key (unique) to each list item based upon when it was created so that I can highlight only the changed element?
class Input extends Component {
render() {
let elems = null;
let str = Array.from(this.props.value);
elems = str.map((ch, i, arr) => (
<span key={ch + arr[i - 1]} className="ch"> // .ch has animation to change color from red to black
{ch}
</span>
));
return (
<div className="fancyText">
<input
type={this.props.type}
value={this.props.value}
placeholder={this.props.placeholder}
onChange={this.props.onChange}
/> // this one is transparent
<div className="highlighted"> // this is visible
{elems}
</div>
</div>
);
}
}
here is the Sandbox Link*
To accomplish what you need, you can use a simply npm package called uuid.
In the class component first import it.
import { v4 as uuidv4 } from 'uuid';
Then you have to assign unique id for the very first parent tag of the component which will you're gonna reuse.
<span key={uuidv4()} className="ch">
{ch}
</span>
If you have a custom key then you can use it with uuid like this,
<span key={uuidv4(YOUR_CUSTOM_KEY)} className="ch">
{ch}
</span>
Hope this help you.

Is there a way to mark as "new" for a Div when it is just generated, and change the flag after it's been touched?

I am using Angular 8 to generate content-editable Divs(drag-able) thru a button, and I'd like to have an indicator to mark the new generated ones before they are touched, so that I can apply some logic accordingly.
And when the new ones has been touched, the new indicator should be removed.
Is there a way to do so?
The generated Div is like below:
<div id="div_id" cdkDrag >
<div (focusout)="blur()" class="contentContainer" [contentEditable]="isDragDisable" >
{{content}}
</div>
</div>
Yes, its possible using JS
$('#div_id').html(data);
data is the new text
then to change the status when clicked, find the code below
$("#div_id").click(function() {
$("#div_id").html("");
}

Vue.js weird rendering with class binding

So basically I have an html table with a v-for that loops over an array of objects, nothing complicated here. When the user clicks on a row, it toggles the selected property of the object "linked" to the given row.
Now, I also setup a simple class-binding with v-bind:class="{'active': n.selected}" where n is my object, but it doesnt update. Now what is more weird is that I use the webpack template from the vue-cli, and when I :
Select a bunch of rows (no active class binded)
update code
hit F5 which triggers the webpack hot-reload
The selected rows suddenly get the active class, as the CSS says so, until the webpack hot-reload has regenerated the page.
Here is my code :
<tr
v-for="n in node.children"
track-by="id"
v-on:click="toggleElement(n)"
v-bind:class="{'active': n.selected}">
<td>
<i class="icons">
<i class="folder yellow icon"></i>
</i>
</td>
<td><a v-on:click.stop="getLevel(n.id)">{{ n.name }}</a></td>
<td><span v-if="n.modification_date">{{ n.modification_date | moment "calendar"}}</span><span v-else>-</span></td>
<td><span v-if="n.size">{{ n.size }}</span><span v-else>-</span></td>
And for the javascript method
toggleElement: function(element) {
element.selected = !!(element.selected === undefined || !element.selected);
},
Now some more details, the objects are being retrieved by an ajax call, and the selected property doesnt exist from the start.
Any advice or solution ?
Thanks a lot!
by default,vue.js can not track the property you add after vue instance had been created
BUT,there is some method you can call to tell vue that new properties been added:
vm.$set('selected', true) //this will make "selected" propertie
trackable
First, do not forget to add the closing </tr> in your code.
Second, you can not add new attributes on objects after creating them with VueJS.
If when creating the element, its attribute selected was undefined, then VueJS will not add the getter/setter on it and changing it won't update the interface.
You will need to add the selected attribute before passing it to VueJS.

Accessing dynamic element id's in AngularJS

I am working with an app that has an ng-repeat that populates a navigation sidebar with a list of items from a Mongo DB. The ng-repeat also populates a series of option buttons for each item. A couple of these option buttons share a dynamic id for each iteration in the ng-repeat. What should be happening here is when I click on one of these buttons, it would change the button 'text' and display some additional options under the menu item and toggle back when clicked again.
Here is my code for these buttons:
<span>
<button ng-hide="highlightItem()" ng-click="showTopic()" ng-attr-id="{{ 'category' + subject._id }}" class="add-button"><i class="fa fa-chevron-down"></i></button>
<button ng-click="hideTopic()" ng-show="highlightItem()" ng-attr-id="{{ 'category' + subject._id }}" class="add-button"><i class="fa fa-chevron-up"></i></button>
</span>
The issue that I am having is that I cannot seem to figure out how to access that dynamic id in my controller. I have code in place that will change the button between the ng-show and ng-hide, but it does it for all iterations of ng-repeat.
This is currently how I am attempting to access the dynamic id. I am not getting any errors, but when I try to use this in my function it doesn't work.
$scope.subjectList = subjects.get({});
var topicButton = document.getElementById('topic' + $scope.subjectList._id);
I have also tried
var topicButton = document.getElementById('topic' + $scope.subject._id);
What is the best way to access the dynamic id in Angular/Javascript? I do not want to use jQuery with this if at all possible.
First and foremost, never manipulate the DOM within an angular controller! It is bad practice. Also, it is bad practice to evaluate methods in ngShow/ngHide.
If I understand you correctly, you're trying to get the subject_id for some reason when the button is clicked. Why can't you just pass back either the id or the entire subject to your method? Then your html would look something like this:
<span>
<button ngClick="toggleTopic(subject)" class="add-button">
<i class="fa" ng-class="{'fa-caret-down': subject.hidden, 'fa-caret-up': !subject.hidden}"></i>
</button>
</span>
Then in your controller you could write something like this:
$scope.toggleTopic = function(subject) {
subject.hidden = !subject.hidden;
};
Using the hidden attribute of your subjects, you can now show or hide elements of your dropdown with ngShow/ngHide like so:
<p ng-bind="subject.descripton" ng-hide="subject.hidden"></p>
This way, you don't have to search the DOM for elements at all.

Categories

Resources