Vue JS this.$set not updating reactivity - javascript

I have a v-for rendered table that has products in it. This table has a column for "active" status where i want the user to be able to click the active button and it becomes inactive or click the inactive button and it becomes active (a toggle switch basically).
I have this implemented by making a POST call to an api route where my status is updated. This works fine.
The problem is that I cannot get vueJS to update the affected object in the array more than once. this.$set works ONE time. If I hit the toggle switch a second time, it no longer works.
This is my table:
<table class="table table-striped fancy-table">
<thead>
<tr>
<th class="text-center"> </th>
<th>Product/Service</th>
<th class="text-center">Status</th>
<th class="text-center">Date Added</th>
<th class="text-center">QTY Sold</th>
<th class="text-center"> </th>
</tr>
</thead>
<tbody>
<tr v-for="(service, index) in services">
<td class="text-center">
<img v-if="service.featured_image" :src="service.featured_image" class="table-thumb">
<img v-else src="/img/default-product.png" class="table-thumb">
</td>
<td>{{service.service_name}}<span class="secondary">${{parseFloat(service.price).toFixed(2)}}</span></td>
<td class="text-center">
<i class="fas fa-circle active"></i>
<i class="fas fa-circle inactive"></i>
</td>
<td class="text-center">{{ service.created_at | moment("dddd, MMMM Do YYYY") }}</td>
<td class="text-center">10</td>
<td class="text-center"><i class="fal fa-edit table-action-btn"></i></td>
</tr>
</tbody>
</table>
This is my method controlling the update:
updateStatus: function(service, index, status) {
// Grab the authorized user
const authUser = JSON.parse(window.localStorage.getItem('authUser'))
// Add a role and refresh the list
this.$http.post('/api/vendor/services/update/' + service.id + '?updateActive=' + status, { }, { headers: { Authorization: 'Bearer ' + authUser.access_token } }).then((response) => {
// update this service
this.$set(this.services, index, response.body);
}).catch(function(error){
console.log(error);
})
}
EDITS:
I've added a :key param to the v-for table, but I'm still getting the same issue. As you can see in my network panel, the first time you click the button, it goes as expected. Posts to the api route with updateActive=0. The second time you click the button it posts to the api route with updateActive=1 and the data is changed server side, but at this point, my object in the services array is not updated, so it now just continually posts with updateActive=1 rather than showing my other button (toggle switch like i'm wanting).
Here's a screenshot of my network panel with 4 clicks on the button.

The problem here might be with a missing key in your v-for
<tr v-for="(service, index) in services">
you could use the key from the index, but in some occasions that may also cause an issue (for example, when removing a single item in an array, the last DOM object will be removed, because the keys shift over)
<tr v-for="(service, index) in services" :key="index">
Ideally you could use something unique from the data like
<tr v-for="(service, index) in services" :key="service.id">

Thanks to #Daniel above, the answer was that I was expecting a number value back (as it is set to an integer in MySQL) and my ajax call was returning that field as a string.

Related

Substitute text in html table generated with angularjs with icon on-the-fly

I have a html page that contains a table with date, staffID and shift. Shift has a value of 0 (morning) or 1 (afternoon). It is generated as follows:
function staffSchedule ($scope, $http) {
$http
.get('/api/schedule/2017/02')
.then(function(response) {
var data = response.data;
$scope.shifts = data;
console.log(data);
})
.catch(function(errorResponse) {
console.log('Error: ' + errorResponse.status);
});
}
<table>
<tr>
<th>Date</th>
<th>StaffID</th>
<th>Shift</th>
</tr>
<tbody ng-repeat="shift in shifts">
<tr>
<td>{{shift.staffid}}</td>
<td>{{shift.date}}</td>
<td>{{shift.type}}</td>
</tr>
</tbody>
</table>
The code returns the data as it should do but I want to extend it to do something more.
I want the code to dynamically substitute the 0 for Fontawesome sun <i class="fa fa-sun-o" aria-hidden="true"></i> and 1 for Fontawesome moon <i class="fa fa-moon-o" aria-hidden="true"></i>. Any idea how I can do this with my current setup please?
Here is what I did in my code to get it to work.
<td>
<i class="fa" ng-class="{'fa-hourglass-start':schedule.shift==0,
'fa-hourglass-end':schedule.shift==1}">
</i>
</td>
To explain:
Grab my icons from font awesome http://fontawesome.io/
ng-repeat receives requests from a RESTful call via AngularJS
one of the fields in the record returns '0' or '1' (always; required field)
Use fa-hourglass-start if 0 and fa-hourglas-end if 1.
Use ng-class.
Something like:
<td>
<i class="fa" ng-class="{'fa-sun':shift.type==0,'fa-moon':shift.type==1}"></i>
</td>
Note I did not verify the proper FA class names

Meteor JQuery Start Rating Issue

I am getting issues trying to use the follow Meteor Package: dandv:jquery-rateit.
I am using it for a ticket system where each update will have a start rating. But when I have more than 1 comment the second one always return 0 value.
Here is my code:
JS:
Template.rating.events({
'click .rateit': function(e){
var rating = $('#rate').rateit('value');
console.log(rating);
Session.set('UpdateId', this._id);
var UpdateId = this._id;
console.log(UpdateId);
/*Meteor.call('saveRate',rating,UpdateId);*/
return false;
}
});
Template.rating.rendered = function () {
this.$('.rateit').rateit();
}
HTML:
<template name="Update">
{{#each Updates}}
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title" align="center">Update</h3>
</div>
<div class="panel-body">
<table class="table">
<tr>
<td width="15%" nowrap align="left">Created By:</td>
<td width="35%" align="left">{{createdBy.username}}</td>
<td width="15%" nowrap align="left">Created At:</td>
<td width="35%" align="left">{{formatDate createdAt}}</td>
<td width="15%" nowrap align="left">Rating:</td>
<td width="35%" align="left">{{> rating}}</td>
</tr>
<tr>
<td colspan="4">{{description}}</td>
</tr>
</table>
</div>
</div>
{{/each}}
</template>
<template name="rating">
<div class="rateit" id="rate"></div>
</template>
Now when I try to rate the second comment is return 0. Screenshot: http://screencast.com/t/ejaTI98X
No matter what star do I select, it always return 0. I think that the error should be probably in the HTML.
I really appreciate all your help on it. If you have any question just let me know.
Have a great day.
You iterate over Updates and I assume there are more than one update. For each update you call the template {{>rateit}} which then renders a div with id=rate, so you will end up with several divs with the same id, so you don't really know which one $('#rate') you are accessing.
In your event handler you also use the global jQuery handler.
I suggest you use the following pattern instead to scope jQuery local to the templates context
Template.rating.events({
'click .rateit': function(e,template){
var rating = template.$('.rateit').rateit('value');
console.log(rating);
Session.set('UpdateId', template.data._id);
var UpdateId = template.data._id;
console.log(UpdateId);
/*Meteor.call('saveRate',rating,UpdateId);*/
return false;
}
});

how to bind data from dynamically named inputs inside a ng-repeat

my goal is to be able to copy data from a table row to another table row.
if the data from 2015 has not changed from 2016 the user needs a quick way of copying the values into the 2016 input fields. the models are dynamically created for these forms. the data you see in this image is assigned to a section. the input models are name 'price_min + section_id', price_max + section_id' , etc...
the history model does not have the section_id added to the end of the model names. so there needs to be a mapping function that i need help with. I need to map the history values to the current model convention and update the view with the values.
currently i have a click function that brings in the matched section history. here is a screen shot of what that looks like.
in that same function i have the 2016 object array with the current model naming convention.
i need to copy the history values into the inputArray. how i go about doing this, i dont know? I have complete control on how this works. and in the plunker you will see how i did this. if i need to change something else to make this work then that is ok. javascript, jquery, lodash, linq.js is currently being used in project.
working plunker
working plunker
$scope.copyHistoryData = function (section) {
var selected = Enumerable.From(sectionsHistory).Where("x => x.section_id == '" + section.section_id + "'").ToArray();
selected = selected[0];
var inputArry = section.sectionInputs;
};
I'm not sure why you use such complex data structure, but here is my take on it
$scope.copyHistoryData = function (section, input) {
var historyId=input.model.split('-')[0];
var historyVal=section.sectionHistory[section.sectionHistory.length-1][historyId];
$scope.model[input.model]=historyVal;
};
To fill all fields:
$scope.copyHistoryData = function (section) {
angular.forEach(section.sectionHistory[section.sectionHistory.length-1], function (historyVal, historyId) {
var inputModel=historyId+"-"+section.section_id;
$scope.model[inputModel]=historyVal;
});
};
http://plnkr.co/edit/OOEmgzKB1pqKjSJMayVF?p=preview
I agree with #ssh. The data structure is a mess. I think this is a better representation of what it should look like. Probably not the best but you shouldn't have to iterate through the data to then display it like that.
http://plnkr.co/C9DWV1dSvkk8lcYdm0An?p=preview
<div class="hpanel" ng-repeat="section in sections">
<div class="panel-body">
<div class="row">
<div class="col-xs-12">
<ul class="list-inline">
<li>
<h5>
<b>SecID</b>
</h5>
<span>{{section.section_id}}</span>
</li>
<li>
<h5>
<b>Section Name</b>
</h5>
<span>{{section.section_name}}</span>
</li>
</ul>
<hr/>
<button ng-click="section.new_section_history = section.section_history">copy row</button>
<table>
<tr>
<td ng-repeat="label in labelIndex">
{{label.label}}
</td>
</tr>
<tr>
<td ng-repeat="label in labelIndex">
{{section.section_history[label.index]}}
</td>
</tr>
<tr>
<td ng-repeat="label in labelIndex">
<input ng-model="section.new_section_history[label.index]"/>
</td>
</tr>
<tr>
<td ng-repeat="label in labelIndex">
<button ng-click="section.new_section_history[label.index] = section.section_history[label.index]">copy</button>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
I have checked your code, and I agree with #Steven Kaspar, the anchors in every row doesn't make much sense. I have solved it using jQuery (I know it doesn't follow your scheme with Angular, but it's another solution).
I have added a new <tr> to add a button inside it.
Check this out:
<tr>
<td colspan="10"><button class="copyRow">Copy complete row</button></td>
</tr>
And in the app.js:
$(document).on("click", ".copyRow", function(){
var $btn = $(this),
$tbody = $btn.parent().parent().parent(),
$trs = $tbody.find("tr");
$.each($trs.eq(0).find("td"), function(index, td){
$trs.eq(1).find("td").eq(index).find("input").val(parseFloat($(td).text().replace("$", "")));
});
})
Here is the updated plunker. I hope it helps

Rails+jQuery: Get the Value of an Item Inside a Table to Use in a Prepend Function

Say I have a table:
<table class="table table-bordered table-hover employes_list_panel">
<thead>
<tr>
<th></th>
<th></th>
<th class="center">Name <i class="fa fa-angle-double-down"></i></th>
<th class="center">IQ <i class="fa fa-angle-double-down"></i></th>
<th class="center">Efficiency <i class="fa fa-angle-double-down"></i></th>
<th class="center">Focus <i class="fa fa-angle-double-down"></i></th>
<th class="center">Happiness <i class="fa fa-angle-double-down"></i></th>
<th class="center">Quality <i class="fa fa-angle-double-down"></i></th>
<th class="center">Salery <i class="fa fa-angle-double-down"></i></th>
</tr>
</thead>
<tbody>
<%Employe.where(company_id: company.id, request: false).each do |employe|%>
<tr>
<td class="center cd-popup-trigger" id="popup3"><i style="color: red;" class="fa fa-close"></i></td>
<td class="center cd-popup-trigger" id="popup4"><i style="color: green;" class="fa fa-arrow-up"></i></td>
<td class="center"><%=employe.name%></td>
<td class="center"><%=employe.iq%></td>
<td class="center"><%=employe.efficiency%></td>
<td class="center"><%=employe.focus%></td>
<td class="center"><%=employe.happiness%></td>
<td class="center"><%=employe.quality.capitalize%></td>
<td class="center"><%=employe.salery%></td>
</tr>
<%end%>
</tbody>
</table>
And I want to get the value of the employe name <%=employe.name%> when popup3 for example is clicked. Note there can be more than one "employe" in the list. How can I get the value of the employe that is on the line where the user clicked the popup, so that I can prepend it using jQuery into the popup <p>.
Ex:
Click to Open Popup | Kevin |...
Click to Open Popup | Sam |...
I need to get the value Sam when I click the "second popup", the
problem is all popups currently have the same id.
Thank you.
Given your code, popup3 (and popup4 for that matter) is gonna be present multiple times on the page (once per tr). You need to give it a class, not an id. I personally like to prefix my JavaScript classes (not used for styling) with js-. In your HTML:
<td class="center cd-popup-trigger js-popup3"><i style="color: red;" class="fa fa-close"></i></td>
You also need to identify the cells which contain the employee name (you'll see why later).
<td class="center js-employee-name"><%= employe.name %></td>
In your JS code, you first need to select the all the td with the js-popup3 class:
$('js-popup3')
Then, you want to trigger something when an event occurs on the element you selected. With jQuery, you would do it like this:
$('js-popup3').on('click', function() {});
Lastly, you want to describe what should be done when the event occurs. This is done in the callback function.
$('.js-popup3').on('click', function() {
# employeeName is the value you want
employeeName = $(this).siblings('.js-employee-name').text();
});
I invite you to read a lot about JS (try to code without jQuery first). Once you feel confident with it, start looking at jQuery.
You can create a click function for the Table tr's and get the value of the third column (index 2):
$('table.reference.notranslate tbody').delegate("tr", "click", function(){
var YourEmployeName = $(this).find('td').eq(2).text();
alert(YourEmployeName);
});
To make the popups ahve unique id's according to employee try something like this:
">
Then I presume you have some js somewhere to handle the popup clicks. This js can extract the employee id with
emp_id = my_clicked_popup_trigger.attr("id").split('-')[-1]

AngularJS - Building a dynamic table based on a json

Given a json like this:
{
"name": "john"
"colours": [{"id": 1, "name": "green"},{"id": 2, "name": "blue"}]
}
and two regular html inputs:
<input type="text" name="name" />
<input type="text" name="color" />
<input type="submit" value="submit" />
I need to build a table with all the possible variations, ex:
John green
John blue
That means that if a user continues adding values through the inputs new rows will appear building the new variations, for instance:
I also need to have available the id to handle it, and I need that when I add new values using the inputs for instance: "Peter" "Black", I need to autofill the id (colour id) dynamically like an auto increment in mysql, resulting in something like this:
{
"colours": […...{"id": 3, "name": "black"}]
}
Is that possible? Which options do I have for doing that with angular? I'm still thinking in the jQuery way and I would like to do it in the angular way.
I took a look to hg-repeat, and used it, but I'm not figuring out how to deliver the expected result, the only thing that come to my mind was to use nested ng-repeats, but it didm´t work.
Thanks so much in advance,
Guillermo
Just want to share with what I used so far to save your time.
Here are examples of hard-coded headers and dynamic headers (in case if don't care about data structure). In both cases I wrote some simple directive: customSort
customSort
.directive("customSort", function() {
return {
restrict: 'A',
transclude: true,
scope: {
order: '=',
sort: '='
},
template :
' <a ng-click="sort_by(order)" style="color: #555555;">'+
' <span ng-transclude></span>'+
' <i ng-class="selectedCls(order)"></i>'+
'</a>',
link: function(scope) {
// change sorting order
scope.sort_by = function(newSortingOrder) {
var sort = scope.sort;
if (sort.sortingOrder == newSortingOrder){
sort.reverse = !sort.reverse;
}
sort.sortingOrder = newSortingOrder;
};
scope.selectedCls = function(column) {
if(column == scope.sort.sortingOrder){
return ('icon-chevron-' + ((scope.sort.reverse) ? 'down' : 'up'));
}
else{
return'icon-sort'
}
};
}// end link
}
});
[1st option with static headers]
I used single ng-repeat
This is a good example in Fiddle (Notice, there is no jQuery library!)
<tbody>
<tr ng-repeat="item in pagedItems[currentPage] | orderBy:sortingOrder:reverse">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.description}}</td>
<td>{{item.field3}}</td>
<td>{{item.field4}}</td>
<td>{{item.field5}}</td>
</tr>
</tbody>
[2nd option with dynamic headers]
Demo 2: Fiddle
HTML
<table class="table table-striped table-condensed table-hover">
<thead>
<tr>
<th ng-repeat="header in table_headers"
class="{{header.name}}" custom-sort order="header.name" sort="sort"
>{{ header.name }}
</th>
</tr>
</thead>
<tfoot>
<td colspan="6">
<div class="pagination pull-right">
<ul>
<li ng-class="{disabled: currentPage == 0}">
<a href ng-click="prevPage()">« Prev</a>
</li>
<li ng-repeat="n in range(pagedItems.length, currentPage, currentPage + gap) "
ng-class="{active: n == currentPage}"
ng-click="setPage()">
<a href ng-bind="n + 1">1</a>
</li>
<li ng-class="{disabled: (currentPage) == pagedItems.length - 1}">
<a href ng-click="nextPage()">Next »</a>
</li>
</ul>
</div>
</td>
</tfoot>
<pre>pagedItems.length: {{pagedItems.length|json}}</pre>
<pre>currentPage: {{currentPage|json}}</pre>
<pre>currentPage: {{sort|json}}</pre>
<tbody>
<tr ng-repeat="item in pagedItems[currentPage] | orderBy:sort.sortingOrder:sort.reverse">
<td ng-repeat="val in item" ng-bind-html-unsafe="item[table_headers[$index].name]"></td>
</tr>
</tbody>
</table>
As a side note:
The ng-bind-html-unsafe is deprecated, so I used it only for Demo (2nd example). You welcome to edit.
Here's an example of one with dynamic columns and rows with angularJS: http://plnkr.co/edit/0fsRUp?p=preview
TGrid is another option that people don't usually find in a google search. If the other grids you find don't suit your needs, you can give it a try, its free
Check out this angular-table directive.
<table class="table table-striped table-condensed table-hover">
<thead>
<tr>
<th ng-repeat="header in headers | filter:headerFilter | orderBy:headerOrder" width="{{header.width}}">{{header.label}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in users" ng-class-odd="'trOdd'" ng-class-even="'trEven'" ng-dblclick="rowDoubleClicked(user)">
<td ng-repeat="(key,val) in user | orderBy:userOrder(key)">{{val}}</td>
</tr>
</tbody>
<tfoot>
</tfoot>
</table>
refer this https://gist.github.com/ebellinger/4399082
First off all I would like to thanks #MaximShoustin.
Thanks of you I have really nice table.
I provide some small modification in $scope.range and $scope.setPage.
In this way I have now possibility to go to the last page or come back to the first page.
Also when I'm going to next or prev page the navigation is changing when $scope.gap is crossing. And the current page is not always on first position. For me it's looking more nicer.
Here is the new fiddle example:
http://jsfiddle.net/qLBRZ/3/

Categories

Resources