Automatically resizing font-size in ng-repeat - javascript

I am currently trying to resize font-size to fit inside a fixed size div. I found this interesting piece of javascript code doing the job very well
$('#defect-text div').css('font-size', '1em');
while ($('#defect-text div').height() > $('#defect-text').height()) {
$('#defect-text div').css('font-size', (parseInt($('#defect-text div').css('font-size')) - 1) + "px");
}
For the following HTML code
<div ng-repeat="defect in surface.defects" class="new-page">
<div id="defect-text">
<div>
<div id="defect-title">{{ 'TYPE' | translate }}:</div>
{{ defect.type }}
<div id="defect-title">Emplacement du défaut:</div>
{{ defect.location }}
<div id="defect-title">Dimension du défaut:</div>
{{ defect.dimension }}
<div id="defect-title">Actions suggérées:</div>
{{ defect.future_operation }}
<div id="defect-title">Détail technique du défaut:</div>
{{ defect.description }}</div>
</div></div>
However, since the different div are inside an ng-repeat expression, my piece of javascript code will resize the font of every container with the desired size of the first one. Is there a way to change to code to select the current element of the ng-repeat and change only it's own css to fit the container?
thanks in advance

I think your problem is actually one of scope:
$('#defect-text div').css('font-size'...)
You're selecting ALL <div> descendants (no matter how deep) of the id="defect-text" element and modifying them at this time. Technically an ID MUST be unique (behavior is not well defined if you have multiple elements with the same ID), and I think your selector is grabbing ALL of the <div> contained within ANY #defect-text elements, which is probably a lot broader than you want, inside of this particular loop.
I would recommend modifying your ng-repeat block and replacing the ID using the angular expression language like
<div id="{{ defect.id }}">
then you can use $("#"+defect.id + " div") to get all <div> descendants of a unique defect.
I'd also consider using the child selector "> div" instead of all descendants.

Related

How can we add before after css code dynamically with vue?

I am trying to solve a problem which I actually never faced. I am trying apply dynamic color from my data inside v-for loop. Now normal css properties are easily appliable. I need to apply css for after. I tried
<div class="_tmln_shdl_crd_itm" v-for="(t, i) in timeLine" v-if="timeLine.length">
<div class="_tmln_shdl_itm_lft">
<p :style="`color:${t.color}`">{{t.time}}</p> // THIS WORKS
</div>
<style>
._tmln_shdl_itm_r8_one h4:after{ // TRIED TO WRITE CSS WITH FOR LOOP BUT FAILED :D
color: t.color
}
</style>
<div>
So how can I make it work for ._tmln_shdl_itm_r8_one h4:after ?
Any solution?
Thank you.
The style you create in the loop creates CSS for the same selector every time. The browser only has one applicable CSS styling for this selector.
With the same selector, the newest CSS wins and is applied. This is CSS Specificity ("When multiple declarations have equal specificity, the last declaration found in the CSS is applied to the element.").
You can create classes dynamically:
<div class="_tmln_shdl_crd_itm" v-for="(t, i) in timeLine" v-if="timeLine.length">
<div :class="`_tmln_shdl_itm_lft _tmln_shdl_itm_lft-${i}`">
<h4>{{ t.time }}</h4>
</div>
<style>
._tmln_shdl_itm_lft-{{ i }} h4:after{
color: {{ t.color }};
}
</style>
<div>
Something like that should work. Making the class dynamic with ._tmln_shdl_itm_lft-{{ i }} makes the magic work.
This also adds a lot of CSS fluff to the page which is probably not what you want in a live environment.
Remarks:
You also should output t.color like this (probably just a mistake in your example):
color: {{ t.color }};
There is also no h4 tag in your example code that the CSS could apply for, but that's probably somewhere else in your page.. hopefully?

CSS and Dynamic Content

UPDATE: Messed up with my CSS, as nothing to do with dynamic content.!
The answer is very informative though!
I'm creating tags and inserting content with handlebars:
Handlebars code:
{{#each docs}}
<article class="first">
<p class="date">
{{#date}} {{date}}
</p>
<h4 class="header">{{#venue.title}} {{venue.title}}
- {{#venue.city}} {{venue.city}}</h4>
<p class="details">
{{#description}} {{description}}
</p>
</article>
{{/each}}
If I just list articles, the CSS works - but when I let handlebars dynamically create them, it won't apply.
CSS code:
div.gig-items{
article: nth-of-type(n +2);
height: 0;
padding: 0;
}
Is there a way to first create the content and then apply CSS or some more elegant solution?
The CSS is applied automatically at each repaint. So inserting your code in the source html or dynamically with js doesn't matter, the css will be applied correctly.
The problem come for sure from your code which has several errors...
First the CSS
You want to style every div elements with class gig-items, but
there is no div and no class gig-items in your template...
You define a property article with value nth-of-type(n+2), but this is not a property, this is a selector
you should use it like :
article:nth-of-type(n+2) {
color: blue;
}
Then in your handlebar template :
I assume you want to insert values by their names, but you use the
prefix # which is reserved for handlebar's loop values like
#index or #keys
Another error is that you write double handlebar
block to display the value. One is enough.
The correct way to insert value is :
<p class="date">{{date}}</p>

How to set the id attribute of a HTML element dynamically with angularjs (1.x)?

Provided an HTML element of type div, how to set the value of its id attribute, which is the concatenation of a scope variable and a string ?
ngAttr directive can totally be of help here, as introduced in the official documentation
https://docs.angularjs.org/guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes
For instance, to set the id attribute value of a div element, so that it contains an index, a view fragment might contain
<div ng-attr-id="{{ 'object-' + myScopeObject.index }}"></div>
which would get interpolated to
<div id="object-1"></div>
This thing worked for me pretty well:
<div id="{{ 'object-' + $index }}"></div>
In case you came to this question but related to newer Angular version >= 2.0.
<div [id]="element.id"></div>
A more elegant way I found to achieve this behaviour is simply:
<div id="{{ 'object-' + myScopeObject.index }}"></div>
For my implementation I wanted each input element in a ng-repeat to each have a unique id to associate the label with. So for an array of objects contained inside myScopeObjects one could do this:
<div ng-repeat="object in myScopeObject">
<input id="{{object.name + 'Checkbox'}}" type="checkbox">
<label for="{{object.name + 'Checkbox'}}">{{object.name}}</label>
</div>
Being able to generate unique ids on the fly can be pretty useful when dynamically adding content like this.
You could just simply do the following
In your js
$scope.id = 0;
In your template
<div id="number-{{$scope.id}}"></div>
which will render
<div id="number-0"></div>
It is not necessary to concatenate inside double curly brackets.
Just <input id="field_name_{{$index}}" />
If you use this syntax:
<div ng-attr-id="{{ 'object-' + myScopeObject.index }}"></div>
Angular will render something like:
<div ng-id="object-1"></div>
However this syntax:
<div id="{{ 'object-' + $index }}"></div>
will generate something like:
<div id="object-1"></div>

Trying to set ng-class of AngularJS accordion-heading does nothing

I have an accordion where some of the entries need to have a heading that emphasizes that the data in that group shows a problem that needs attention. I tried using a "accordion-heading" with a "ng-class" for "has-error" (from bootstrap) that is conditional on the method that determines whether there is something that needs attention. I've tried several variations of this, and the "class" attribute never gets rendered in the HTML.
This is an excerpt from my HTML:
<accordion close-others="false">
<div ng-repeat="(name, dataSource) in dataSourceMap">
<accordion-group>
<accordion-heading>
<span ng-class="{'has-error': anyFailuresInList(dataSource)}">
{{name}}
</span>
</accordion-heading>
{{anyFailuresInList(dataSource)}}
</accordion-group>
</div>
</accordion>
The example in the documentation at enter link description here indicates that this should be possible, even though the example is a little broken (it appears to put the class on an empty "i" element).
accordion-group doesn't support ng-class well.
Please use class instead.
If you want to set the style conditionally, you can use the ? operator. For instance:
<uib-accordion-group class="{{isSpecial ? 'special-style':''}}">
This can be done by applying a css class to the group instead of the header directly
<div accordion-group is-open="node.active" is-disabled="node.active" ng-class="{'panel-active': node.active}" ng-repeat="(key,node) in nav.nodes">
<div accordion-heading >{{node.title}}</div>
</div>
Then in your css change the back for the first DIV
.panel-active>div {
background-color: #YourActiveColor;
border-color: #YourActiveColor;
}

Why is this "this + sibling" call failing?

I am having some trouble with what I thought should be a simple sibling selector in jQuery.
The problem generates no error message, of course, it simply fails to select properly. Inside a document(ready) function() I have the following simple code to first hide all of the popups, then wait for a person to click an image which will show the sibling pop-up:
//hide all the charm pop ups
$(".charm_pop").hide();
$(".charm > img").click(function() {
$("this + .charm_pop").show();
})
My HTML is being generated by a Django for loop, so there will be many iterations of this simple image/popup combo markup:
{% for ch in charms %}
<div class="charm">
<img src="{{ MEDIA_URL }}images/charms/{{ ch.image }}" alt="{{ ch.name }}" />
<div class="charm_pop">
<p id="charm_name">{{ ch.name }}</p>
<p id="charm_desc">{{ ch.description }}</p>
<p id="charm_price">${{ ch.price }}</p>
<form method="post" action="." class="cart">{% csrf_token %}
<p>**some inputs and what not</p>
</form>
</div>
</div>
{% endfor %}
As you can see, I simply wait for an image to be clicked, and when it is I select it's sibling and reveal that corresponding pop-up. Yet when I click an image, nothing happens. If I replace $("this + .charm_pop").show(); with $(".charm_pop").show(); it does indeed show all of the pop-ups, so the click function is working, the selector is just wonky.
Am I misunderstanding how this is working in this context?
When writing jQuery selectors the string "this" simply means "an HTML element <this>", so $("this + .charm_pop") will certainly not work.
Concatenating the string representation of an object with something else is also not meaningful here, so $(this + " .charm_pop") will also not work.
You should be using appropriate traversal functions instead, starting from $(this):
$(this).next(".charm_pop").show();
There is a number of different ways to go from the clicked image to its sibling .charm_pop, but .next() is fastest and also semantically identical to the adjacent-sibling selector + that you are trying to utilize.
Your code is basically using the + css selector
$("this + .charm_pop").show();
element+.class which says "Selects all the .class elements that are placed immediately after element
In your case it is looking for an element named this. I doubt you have any elements with the tag name of <this>.
Your code needs to be
$(this).siblings(".charm_pop").show();
or
$(this).next(".charm_pop").show();
You can do this :
$(this).find(".charm_pop").show();

Categories

Resources