jQuery function in an AngularJS way - javascript

I'm moving some code from jQuery to AngularJS, but I am still having trouble understanding its logic, so switching to AngularJS caused confusion. I have the following example which I did in AngularJS:
<div class="main" ng-repeat="data in mainData">
<div class="row forecast_daily">
<div class="col-md-4 col-md-offset-2">
{{ data.date.weekday }}
{{ data.date.day }}
{{ data.edit }}
</div>
<div class="col-md-3 range-index">
<div class="range">
<span class="min">{{ data.low }}</span>
<span class="max">{{ data.high }}</span>
</div>
</div>
</div>
</div>
But I have this jQuery function which I'm having trouble doing in an AngularJS way.
$('.range-index').each(function(){
var min = parseInt($(this).find('.min').html()),
max = parseInt($(this).find('.max').html());
var l = max - (max-min);
var w = (max-min)*20;
$(this).find('.range').css({left:l+"%",width: w});
});

It looks like ng-style can work well here:
<div class="col-md-3 range-index">
<div class="range"
ng-style="{left: data.low + '%', width: (data.high - data.low) * 20 + 'px'}">
<span class="min">{{ data.low }}</span>
<span class="max">{{ data.high }}</span>
</div>
</div>
Or better. You can create a helper function in the controller:
$scope.getStyle = function(data) {
return {
left: data.low + '%',
width: (data.high - data.low) * 20 + 'px'
};
};
And use it like this:
<div class="col-md-3 range-index">
<div class="range" ng-style="getStyle(data)">
<span class="min">{{ data.low }}</span>
<span class="max">{{ data.high }}</span>
</div>
</div>

Related

Showing changes without refreshing in VueJS

I'm so new in VueJS and still don't control much of its funcionalities,
I'm trying when I update or delete something in my window not need to refresh to see the changes...how can I do it, please?
My functions work perfectly in my controller, just need to solve the question of seeing changes.
Here I post my code if it helps...thank you very much!!
UpdateProfile.vue:
<div class="field">
<label class="label">Schedules</label>
<!--Edit and Delete Schedules-->
<div v-for="schedule in sortedDays(data.schedulesDisplayed)" class="control">
<div class="container">
<div class="field">
<label class="label">Week Day: {{schedule.week_day}}</label>
</div>
<div class="row">
<div class="col">
<div class="row">
Opening Time: <input class="input" type="text" placeholder="Pub schedules" v-model="schedule.opening_time">
</div>
</div>
<div class="col">
<div class="row">
Closing Time: <input class="input" type="text" placeholder="Pub schedules" v-model="schedule.closing_time">
</div>
</div>
</div>
<div class="field">
<div class="buttons is-left">
<div class="button is-info" #click="updatePubSchedule(schedule)">
<span class="icon"><i class="fas fa-save fa-lg"></i></span>
<span>Save</span>
</div>
<div class="button is-danger" #click="deletePubSchedule(schedule)">
<span class="icon"><i class="far fa-trash-alt"></i></span>
<span>Delete Schedule</span>
</div>
</div>
</div>
</div>
</div>
</div>
updatePubSchedule(schedule){
var instance = this;
this.api.put('/pubschedules/update/' + this.data.id, schedule).then(response => {
console.log(schedule);
//data = response.data;
instance = response.data;
});
},
deletePubSchedule(schedule){
var instance = this;
this.api.delete('/pubschedules/delete/' + this.data.id, schedule).then(response => {
//data = response.data;
instance = response.data;
});
},
And in my controller:
/**
* #param Pub $pub
*/
public function updatePubSchedule(Pub $pub)
{
//json_die(request()->all());
Schedule::where([
['pub_id','=', $pub->id],
['week_day' ,'=', request()->get('week_day')]
])->update(request()->all());
}
/**
* #param Pub $pub
*/
public function deletePubSchedule(Pub $pub)
{
Schedule::where([
['pub_id','=', $pub->id],
['week_day' ,'=', request()->get('week_day')]
])->delete();
}
I don't know how you get your initial data but you can have a GET request that gets fresh data from the laravel after you update/delete initial data.
With the response from that request you can update your data.schedulesDisplayed prop.
Important! You need to set the :key attribute in the div that uses v-for rendering like this
<div v-for="(schedule, index) in sortedDays(data.schedulesDisplayed)" :key="index" class="control">
I used the index for the sake of this example but you should use a unique property of sortedDays return.

Group same strings in array and count them

This is an Angular project and I have data that comes from the server. I am creating three lists. I have three types of sentiment and each sentiment has a description
for (const sentiment of this.dataSource.allFavourabilities) {
if (sentiment.sentiment === 'Positive') {
this.positiveList.push(sentiment);
}
if (sentiment.sentiment === 'Negative') {
this.negativeList.push(sentiment);
}
if (sentiment.sentiment === 'Neutral') {
this.neutralList.push(sentiment);
}
}
This is how it looks like when I console.log that.
coverageId:137
description:"Positive"
id:119
sentiment:"Positive"
In HTML I am listing this in three divs and the only constant is sentiment which is displayed as an icon, the description can be different. But client mainly types positive, negative or neutral so I get an ugly list with same data. I want to filter that so it is a matching string in the description and I have positive in the description like 50 times, in my HTML it should say
Preferred outcome:
positive(50)
negative(2)
neutral
and not
positive
positive
positive
positive etc.
negative
negative
neutral
This is my HTML:
<div class="form-item favourability-wrapper flex">
<div class="column" *ngIf="positiveList">
<div class="sentiment-label">Positive</div>
<ng-container *ngFor="let a of positiveList">
<div class="sentiment sentiment-positive ">
<i class="fas fa-smile"></i>
<div class="description">{{ a.description }}</div>
</div>
</ng-container>
</div>
<div class="column" *ngIf="negativeList">
<div class="sentiment-label">Negative</div>
<ng-container *ngFor="let b of negativeList">
<div class="sentiment sentiment-neutral">
<i class="fas fa-meh"></i>
<div class="description">{{ b.description }}</div>
</div>
</ng-container>
</div>
<div class="column" *ngIf="neutralList">
<div class="sentiment-label">Neutral</div>
<ng-container *ngFor="let c of neutralList">
<div class="sentiment sentiment-negative">
<i class="fas fa-frown"></i>
<div class="description">{{ c.description }}</div>
</div>
</ng-container>
</div>
</div>
Change this to a html element that shows the length
<ng-container *ngFor="let a of positiveList">
<div class="sentiment sentiment-positive ">
<i class="fas fa-smile"></i>
<div class="description">{{ a.description }}</div>
</div>
</ng-container>
to
<div *ngIf="positiveList">
{{ '(' + (positiveList?.length || '0')+')' }}
</div>
You can try this:
<div class="form-item favourability-wrapper flex">
<div class="column" *ngIf="positiveList">
<div class="sentiment-label">Positive</div>
<div class="sentiment sentiment-positive ">
<i class="fas fa-smile"></i>
<div class="description">({{ positiveList.length > 0 ? positiveList.length : '0' }})</div>
</div>
</div>
<div class="column" *ngIf="neutralList">
<div class="sentiment-label">Neutral</div>
<div class="sentiment sentiment-neutral">
<i class="fas fa-meh"></i>
<div class="description">({{ neutralList.length > 0 ? neutralList.length : '0' }})</div>
</div>
</div>
<div class="column" *ngIf="negativeList">
<div class="sentiment-label">Negative</div>
<div class="sentiment sentiment-negative">
<i class="fas fa-frown"></i>
<div class="description">({{ negativeList.length > 0 ? negativeList.length : '0' }})</div>
</div>
</div>
</div>
<div class="form-item favourability-wrapper flex">
<div class="column" *ngIf="positiveList">
<div class="sentiment-label">Positive{{ positiveList.length ? '(' + positiveList.length + ')' : '' }}</div>
</div>
<div class="column" *ngIf="negativeList">
<div class="sentiment-label">Negative{{ negativeList.length ? '(' + negativeList.length + ')' : '' }}</div>
</div>
<div class="column" *ngIf="neutralList">
<div class="sentiment-label">Neutral{{ neutralList.length ? '(' + neutralList.length + ')' : '' }}</div>
</div>
</div>
maybe this works for you, you can use Array.length to get the count.
Since you're asking for help, let me help you improve your code :
this.feelings = [
{ name: 'Positive', sentiments : [] },
{ name: 'Negative', sentiments : [] },
{ name: 'Neutral', sentiments : [] },
];
for (const sentiment of this.dataSource.allFavourabilities) {
if (sentiment.sentiment === 'Positive') {
this.feelings[0].sentiments.push(sentiment);
}
if (sentiment.sentiment === 'Negative') {
this.feelings[1].sentiments.push(sentiment);
}
if (sentiment.sentiment === 'Neutral') {
this.feelings[2].sentiments.push(sentiment);
}
}
<!-- I'll let you append the classes to your convenience -->
<div *ngFor="let feeling of feelings">
{{ feeling.name }} : {{ feeling.sentiments.length }}
</div>
See how the HTML got reduced ? We can even reduce the typescript further with this
this.feelings: { name: string, sentiments : any[] }[] = this.dataSource.allFavourabilities
.reduce((p, n) => {
if (!p.find(feeling => feeling.name === n.sentiment)) {
p.push({ name: n.sentiment, sentiments : []});
}
p.find(feeling => feeling.name === n.sentiment).sentiments.push(n);
return p;
}, []);

Push $scope with ng-click AngularJS

I want the user to press the $scope.getJobList button to get the value of the $scope.getIdJobs variable. I am trying to solve this with the push functionality but it is not working.
Am I doing something wrong?
jsfidle
Example
$scope.getIdJobs = function(getIdJobs) {
$scope.getIdJobs = $filter('filter')( $scope.products, {id:getIdJobs})[0];
$scope.getJobList = function(currObj){
$scope.workflows[0].Steps.push($scope.getIdJobs); // It will add a new step ($scope.newStep) in first workflow's Steps
console.log($scope.workflows);
//$scope.workflows.push($scope.getIdJobs); // line that changed
$scope.workflows = job_detail.addJob(currObj)
console.log($scope.workflows);
};
//console.log("Estou dentro do getidJobs " + getIdJobs);
}
html
<article dir-paginate="product in filteredJobs | filter:searchTerm | orderBy: sorting.order:sorting.direction | itemsPerPage:5" id="{{product.id}}" class="productlist product col-sm-12">
<div class="inner-content">
<div class="clearfix">
<div class="col-md-8 col-sm-8 col-xs-8">
<h4 class="productname" name="{{product.Categoria}}"><a data-ng-href="#/job/{{product.id}}" ng-click="getIdJobs(product.id)">{{product.Title}}</a></h4>
<!--<h4 class="productname" name="{{product.Categoria}}"><a data-ng-href="#/job/{{filteredJobs.indexOf(product)}}">{{product.Title}}</a></h4>-->
<h4 style="color: gray;"><a data-ng-href="#/empresa">{{product.Empresa}}</a></h4>
</div>
</div>
</div>
</article>

Changing Button Text on Text Input - JS, RoR

I have a Price Range button that when clicked will open a popover. In that popover, there are two input boxes. A Min Rent box and a Max Rent box. Once values are entered in that box I would like the Price Range box to now display the values that were previously entered. Something like $2000 to $2500. I have looked and looked on Google and other StackOverflow questions and answers, but I am not sure how to accomplish this. Any help would be greatly appreciated!
Erb File:
<div class="col-md-2 col-xs-6">
<a tabindex="0" class="button btn-transparent" id="listing-price-selector" role="button" data-toggle="popover">Price Range <span class="caret"></span></a>
</div>
<div id="listing-price-content" style="display: none;">
<div class="container-fluid">
<div class="row">
<div class="col-xs-6">
<div class="input-group input-group-sm">
<span class="input-group-addon" id="basic-addon1">$</span>
<%= f.text_field :min_price, class: "form-control", placeholder: "Min Rent", data: { "binding-name" => "min-price" } %>
</div>
</div>
<div class="col-xs-6">
<div class="input-group input-group-sm">
<span class="input-group-addon" id="basic-addon1">$</span>
<%= f.text_field :max_price, class: "form-control", placeholder: "Max Rent", data: { "binding-name" => "max-price" } %>
</div>
</div>
</div>
</div>
</div>
JS File:
$('#listing-price-selector').popover( {
html: true,
trigger: 'manual',
placement: 'bottom',
template: '<div class="popover price-range-pop" role="tooltip"><div class="arrow"></div><div class="popover-content"></div></div>',
content: function() {
return $('#listing-price-content').html();
}
});
I think what you need is to trigger a function from a change event (keyup) on either of the inputs inside the popover.
The tricky thing here, at least to me, is that the content of the popover is dynamically generated, so trying to assign an event handler for those elements at page load won't work.
What does work is to assign the event handler once those elements have been generated.
There are events that are fired for the popover itself that you can assign a handler on, and that will get you where you need.
$("[data-toggle='popover']").on('shown.bs.popover', function(){
$("#listing-price-selector").next().find("#min-price").keyup(modPrice);
$("#listing-price-selector").next().find("#max-price").keyup(modPrice);
});
and then modPrice can be a function to fix the value of the price range box. Something like:
function modPrice(){
var mn = $("#listing-price-selector").next().find("#min-price").val();
var mx = $("#listing-price-selector").next().find("#max-price").val();
mn = (mn == "") ? 0 : mn;
mx = (mx == "") ? 0 : mx;
$("#listing-price-selector").text(mn + " to " + mx);
}
In there you can do a lot more processing, such as making sure max is over min, or whatever you like.
Hope this helps!
PS - got the popover events from W3Schools ref page: http://www.w3schools.com/bootstrap/bootstrap_ref_js_popover.asp
PPS - and for fun, I made a snippet example, here it is:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
<div class="col-md-2 col-xs-6">
<a tabindex="0" class="button btn-transparent" id="listing-price-selector" role="button" data-toggle="popover">Price Range <span class="caret"></span></a>
</div>
<div class="input-group input-group-sm">
<span class="input-group-addon" id="basic-addon1">$</span>
<input type="text" id="priceBox" />
</div>
<div id="listing-price-content" style="display: none;">
<div class="container-fluid">
<div class="row">
<div class="col-xs-12">
<div class="input-group input-group-sm">
<label>Min</label>
<span class="input-group-addon" id="basic-addon1">$</span>
<input type="text" id="min-price" />
</div>
</div>
<div class="col-xs-12">
<div class="input-group input-group-sm">
<label>Max</label>
<span class="input-group-addon" id="basic-addon1">$</span>
<input type="text" id="max-price" />
</div>
</div>
</div>
</div>
</div>
<script>
$('#listing-price-selector').popover( {
html: true,
trigger: 'click',
placement: 'bottom',
template: '<div class="popover price-range-pop" role="tooltip"><div class="arrow"></div><div class="popover-content"></div></div>',
content: function() {
return $('#listing-price-content').html();
}
});
$("[data-toggle='popover']").on('shown.bs.popover', function(){
$("#listing-price-selector").next().find("#min-price").keyup(modPrice);
$("#listing-price-selector").next().find("#max-price").keyup(modPrice);
});
$("[data-toggle='popover']").on('hide.bs.popover', function(){
modPrice();
});
function modPrice(){
var mn = $("#listing-price-selector").next().find("#min-price").val();
var mx = $("#listing-price-selector").next().find("#max-price").val();
mn = (mn == "") ? 0 : mn;
mx = (mx == "") ? 0 : mx;
$("#priceBox").val(mn + " to " + mx);
$("#listing-price-selector").text(mn + " to " + mx);
}
</script>

Assign value to dynamically created scope variables

I'm trying to create a means to toggle dynamically created rows of information. I've tried using ng-init, and then passing it to a function, but I'm screwing up somewhere and I can't seem to wrap my head around how or if this is possible. The gap, I believe, is in getting the concatenated scope variable to be referenced elsewhere. I'm using Bootstrap 3 and AngularJS 1.5.
The HTML:
<div class="row" data-ng-repeat="equipment in task.equipment">
<div class="col-md-12">
<h4 class="green-text">
{{ equipment.equipId }}
<small class="green-text">
<i class="glyphicon"
data-ng-class="{'glyphicon-triangle-bottom': field{{ $index }}, 'glyphicon-triangle-right': !field{{ $index }}}"
data-ng-init="equipment['field' + $index] = true"
data-ng-click="toggleTaskEquip('field{{ $index }}')">
field{{ $index }}: I WANT THIS TO WORK</i>
</small>
</h4>
</div>
<div data-ng-show="field{{ $index }}">
...stuff here...
</div>
</div>
The JS:
$scope.toggleTaskEquip = function(toggleBool)
{
if (toggleBool === true)
$scope.isTaskEquipOpen = false;
else if (toggleBool === false)
$scope.isTaskEquipOpen = true;
};
If I understand the problem correctly, you want to be able to toggle the boolean created in the ng-init with a click.
I think you need this:
<div class="container-fluid">
<div ng-controller="MyCtrl">
<div class="row" data-ng-repeat="equipment in task.equipment">
<div class="col-md-12">
<h4 class="green-text">
{{equipment.equipId}}
<small class="green-text">
<i class="glyphicon"
data-ng-class="{'glyphicon-triangle-bottom': isVisible, 'glyphicon-triangle-right': !isVisible}"
data-ng-init="isVisible = true"
data-ng-click="isVisible = !isVisible">I WANT THIS TO WORK</i>
</small>
</h4>
</div>
<div data-ng-show="isVisible">
...stuff here...
</div>
</div>
</div>
</div>
You don't even need the function toggleTaskEquip on the $scope.
JSFiddle here.
ng-repeat creates a new scope for each template instance, so you can just create a separate isVisible for each equipment with isVisible = true in the ng-init.

Categories

Resources