Remove button after 30 minutes. Laravel - laravel

I'm making a discussion forum and I want to remove the users ability to edit the comment they made after 30 mins.
This is the code for my button in the vue.js, it's not a "real" button, it's a clickable icon
<div class="btn-link-edit action-button"
#click="edit(comment)">
<i class="fas fa-pencil-alt"></i>
</div>
method in vue.js
edit(model) {
this.mode = 'Editar';
this.form = _.cloneDeep(model);
this.dialogFormVisible = true;
},
What would be the best way to add this timer, the timer should start right when the user makes the comment, in the table for this I have a field called comment_time with that information.
How can I do this?

The simplest way to do that is:
Here in template:
<div id="app">
<div v-for="comment in comments">
<p>
{{comment.text}}
</p>
<button v-if="commentTime(comment.comment_time)">Edit </button>
</div>
</div>
Vue script:
new Vue({
el: "#app",
data: {
comments: [
{ text: "Nancy comment", comment_time: 1579206552201}
]
},
computed: {
now () {
return new Date()
}
},
methods: {
commentTime(cTime){
let t = new Date(cTime)
t.setMinutes(t.getMinutes() + 30)
return this.now.getTime() < t.getTime()
}
}
})
You can show the result here:
your code in jsfiddle

First, start by putting v-if="canEdit" on your <div>. Then in your script section of the Vue component we're going to create a canEdit boolean, then a loop to update it on a regular basis. This assumes you have a specific Comment.vue component and this.comment is being passed to the component already, maybe as a prop or something, and that it contains the typical Laravel Model fields, specifically created_at.
data() {
return {
canEdit: true, // Defaults to `true`
checkTimer: null, // Set the value in `data` to help prevent a memory leak
createdAt: new Date(this.comment.created_at),
};
},
// When we first make the component, we set `this.createdPlus30`, then create the timer that checks it on a regular interval.
created() {
this.checkTimer = setInterval(() => {
this.canEdit = new Date() > new Date(this.created_at.getTime() + 30*60000);
}, 10000); // Checks every 10 seconds. Change to whatever you want
},
// This is a good practice whenever you create an interval timer, otherwise it can create a memory leak.
beforeDestroy() {
clearInterval(this.checkTimer);
},

Related

Append components inside other component in Vue JS

I'm trying to make a simple "Load More" function for posts using Vue JS but when I try to append new posts, the previous ones are removed.
This is my PostWallComponent, which is supposed to hold all posts (<post-item-component>).
I fetch first 4 posts from the DB, store them in this.posts and then I send them using the v-for loop to <post-item-component>.
Then when someone clicks on the "More" button I call getPosts() function where I fetch another 4 posts from the DB. Here comes my problem - I store these new posts inside this.posts and I try to append them to the post container. They do append but the previous 4 get deleted from the container.
I think I know what is wrong - at line this.posts = response.data I replace old posts with new ones but I don't know how to append new ones without removing old ones. I tried to push() new posts to the array but that turned into a big mess (repetitive posts in the container).
<template>
<div class="container">
<div class="post_container">
<post-item-component v-for="post in this.posts"
v-bind:cuid="cuid"
v-bind:auid="auid"
v-bind:post="post"
v-bind:key="post.id">
</post-item-component>
<button type="button" #click="getPosts">More</button>
</div>
</div>
</template>
<script>
import PostItemComponent from "./PostItemComponent";
export default {
props: ['init_place', 'init_type', 'current_user_id', 'active_user'],
components: {
PostItemComponent
},
data() {
return {
place: this.init_place,
type: this.init_type,
cuid: this.current_user_id,
auid: this.active_user,
limit: 4,
offset: 0,
posts: [],
};
},
mounted() {
console.log('Component mounted.');
this.getPosts();
},
methods: {
getPosts() {
console.log('post');
axios.get('/p/fetch', {
params: {
place: this.place,
type: this.type,
cuid: this.cuid,
auid: this.auid,
offset: this.offset,
limit: this.limit,
}
})
.then((response) => {
console.log(response);
this.posts = response.data;
this.offset = this.limit;
this.limit += 4;
})
.catch(function (error) {
//currentObj.output = error;
});
}
}
}
</script>
In case someone wonders:
cuid is current user id = ID of user whose profile I opened
auid is active user ID = logged in user ID
<post-item-component> is just couple of divs displaying post header, body etc.
you could also use this.posts = this.posts.concat(response.data)
the problem is that the Array.push() method does not work with vue reactivity. For that you need to replace the whole array. As one proposed solution, you could use the spread operator to achieve this as so:
this.posts = [...this.posts, ...response.data];
This is replacing the whole array with a new array that is combining the old items with the fetched ones by spreading each of the array elements into the new array.
You can see an example here:
codesandbox example

Refreshing data after-the-fact in AlpineJS

I'm using Alpine to display a list of items that will change. But I can't figure out how to tell Alpine to refresh the list of items once a new one comes back from the server:
<div x-data=" items() ">
<template x-for=" item in items " :key=" item ">
<div x-text=" item.name "></div>
</template>
</div>
The first "batch" of items is fine, because they're hard-coded in the items() function:
function items(){
return {
items: [
{ name: 'aaron' },
{ name: 'becky' },
{ name: 'claude' },
{ name: 'david' }
]
};
}
Some code outside of Alpine fetches and receives a completely new list of items, that I want to display instead of the original set. I can't figure out how, or if it's even currently possible. Thanks for any pointer.
There are 3 ways to solve this.
Move the fetch into the Alpine.js context so that it can update this.items
function items(){
return {
items: [
{ name: 'aaron' },
{ name: 'becky' },
{ name: 'claude' },
{ name: 'david' }
],
updateItems() {
// something, likely using fetch('/your-data-url').then((res) => )
this.items = newItems;
}
};
}
(Not recommended) From your JavaScript code, access rootElement.__x.$data and set __x.$data.items = someValue
<script>
// some other script on the page
// using querySelector assumes there's only 1 Alpine component
document.querySelector('[x-data]').__x.$data.items = [];
</script>
Trigger an event from your JavaScript and listen to it from your Alpine.js component.
Update to the Alpine.js component, note x-on:items-load.window="items = $event.detail.items":
<div x-data=" items() " x-on:items-load.window="items = $event.detail.items">
<template x-for=" item in items " :key=" item ">
<div x-text=" item.name "></div>
</template>
</div>
Code to trigger a custom event, you'll need to fill in the payload.
<script>
let event = new CustomEvent("items-load", {
detail: {
items: []
}
});
window.dispatchEvent(event);
</script>
Expanding on Hugo's great answer I've implemented a simple patch method that lets you update your app's state from the outside while keeping it reactive:
<div x-data="app()" x-on:patch.window="patch">
<h1 x-text="headline"></h1>
</div>
function app(){
window.model = {
headline: "some initial value",
patch(payloadOrEvent){
if(payloadOrEvent instanceof CustomEvent){
for(const key in payloadOrEvent.detail){
this[key] = payloadOrEvent.detail[key];
}
}else{
window.dispatchEvent(new CustomEvent("patch", {
detail: payloadOrEvent
}));
}
}
};
return window.model;
}
In your other, non-related script you can then call
window.model.patch({headline : 'a new value!'});
or, if you don't want assign alpine's data model to the window, you can simply fire the event, as in Hugo's answer above:
window.dispatchEvent(new CustomEvent("patch", {
detail: {headline : 'headline directly set by event!'}
}));

Vue.JS not update data into nested Component

I'm working with 3 VUE nested components (main, parent and child) and I'm getting trouble passing data.
The main component useget a simple API data based on input request: the result is used to get other info in other component.
For example first API return the regione "DE", the first component is populated then try to get the "recipes" from region "DE" but something goes wrong: The debug comments in console are in bad order and the variable used results empty in the second request (step3):
app.js:2878 Step_1: DE
app.js:3114 Step_3: 0
app.js:2890 Step_2: DE
This is the parent (included in main component) code:
parent template:
<template>
<div>
<recipes :region="region"/>
</div>
</template>
parent code:
data: function () {
return {
region: null,
}
},
beforeRouteEnter(to, from, next) {
getData(to.params.e_title, (err, data) => {
console.log("Step_1: "+data.region); // return Step_1: DE
// here I ned to update the region value to "DE"
next(vm => vm.setRegionData(err, data));
});
},
methods: {
setRegionData(err, data) {
if (err) {
this.error = err.toString();
} else {
console.log("Step_2: " + data.region); // return DE
this.region = data.region;
}
}
},
child template:
<template>
<div v-if="recipes" class="content">
<div class="row">
<recipe-comp v-for="(recipe, index) in recipes" :key="index" :title="recipe.title" :vote="recipe.votes">
</recipe-comp>
</div>
</div>
</template>
child code:
props: ['region'],
....
beforeMount () {
console.log("Step_3 "+this.region); // Return null!!
this.fetchData()
},
The issue should be into parent beforeRouteEnter hook I think.
Important debug notes:
1) It looks like the child code works properly because if I replace the default value in parent data to 'IT' instead of null the child component returns the correct recipes from second API request. This confirms the default data is updated too late and not when it got results from first API request.
data: function () {
return {
region: 'IT',
}
},
2) If I use {{region}} in child template it shows the correct (and updated) data: 'DE'!
I need fresh eyes to fix it. Can you help me?
Instead of using the beforeMount hook inside of the child component, you should be able to accomplish this using the watch property. I believe this is happening because the beforeMount hook is fired before the parent is able to set that property.
More on the Vue lifecycle can be found here
More on the beforeMount lifecycle hook can be found here
In short, you can try changing this:
props: ['region'],
....
beforeMount () {
console.log("Step_3 "+this.region); // Return null!!
this.fetchData()
},
To something like this:
props: ['region'],
....
watch: {
region() {
console.log("Step_3 "+this.region); // Return null!!
this.fetchData()
}
},
Cheers!!

set watcher on props of vuejs component

I have created datepicker vuejs component.
I have set two date picker in page to select start date and end date. So startDate and endDate of one datepicker is depend on value of another. So for that, I need to set watcher on selected value.
It was working fine with VueJS (< 2.3.0) using this.$emit('input', value). But in VueJS (2.4.4), it is not working.
I'm very poor in front-end development. So I got nothing even after spending two days on vuejs. I just come to know that it is just because of vue version > 2.2.6.
If anyone know the solution, it will be realy very much helpful for me.
Here is my code.
DatePicker.vue
<template>
<div class="input-append date form_datetime">
<input size="16" type="text" readonly>
<span class="add-on"><i class="icon-th fa fa-calendar"></i></span>
</div>
</template>
<script>
export default {
twoWay: true,
props: ['slctd', 'startDate', 'endDate'],
mounted: function () {
var self = this;
$(this.$el).datetimepicker({
format: "mm/dd/yyyy",
autoclose: true,
minView: 2,
maxView: 3,
daysShort: true,
initialDate: this.slctd,
startDate: this.startDate,
endDate: this.endDate,
}).on('changeDate', function (ev) {
var value = new Date(ev.date.valueOf()).toString('MM/dd/yyyy');
self.$emit('input', value);
});
},
watch: {
slctd: function (value) {
console.log('watch:value ' + value);
$(this.$el).find("input").val(value);
},
startDate: function (value) {
console.log('watch:start ' + value);
$(this.$el).datetimepicker('setStartDate', value);
},
endDate: function (value) {
console.log('watch:end ' + value);
$(this.$el).datetimepicker('setEndDate', value);
}
},
}
</script>
JS
window.Vue = require('vue');
Vue.component('component-date-picker', require('./components/DatePicker.vue'));
window.app = new Vue({
el: '#app',
data: {
queries: {
start_date: '10/04/2017',
end_date: '10/05/2017',
}
},
});
HTML
<component-date-picker v-bind:end-date="queries.end_date" v-bind:slctd="queries.start_date" v-model="queries.start_date"></component-date-picker>
<component-date-picker v-bind:start-date="queries.start_date" v-bind:slctd="queries.end_date" v-model="queries.end_date"></component-date-picker>
VueJS - 2.2.6 (working)
VueJS - 2.4.4 (not working)
I have also tried this too. But didn't get success
HTML
<component-date-picker v-bind:end-date="queries.end_date" v-bind:slctd="queries.start_date" v-model="queries.start_date" v-on:input="set_start_date"></component-date-picker>
<component-date-picker v-bind:start-date="queries.start_date" v-bind:slctd="queries.end_date" v-model="queries.end_date" v-on:input="set_end_date"></component-date-picker>
JS
methods: {
set_start_date: function(value){
console.log('called');
this.queries.start_date = value;
},
set_end_date: function(value){
this.queries.end_date = value;
},
}
In this code, I method was called and I was getting 'called' in console. But watcher of component not working. It should work as startDate/endDate is changed in these methods. I don't know what is the new working concept of VueJS 2.4.4.
Solved
It was my stupid mistake due to lack of knowledge in front-end. I was re-creating queries object in create method of vue root instance. And in previous code, there was nothing in create so it was working fine.
Here is my mistake
JS
created: function () {
/* my mistake starts */
this.queries = queryString.parse(location.search, {arrayFormat: 'bracket'});
/* my mistake ends */
this.queries.start_date = (this.queries.start_date) ? this.queries.start_date : null;
this.queries.end_date = (this.queries.end_date) ? this.queries.end_date : null;
},

In Backbone Collection, delete a model by link on itself

Im just trying to delete a model from a collection, with a link on itself.
I've attach the event to the "Eliminar button" but it seems Im losing the reference to the model element that contains it... and can't find it.. can you?:
(function ($) {
//Model
Pelicula = Backbone.Model.extend({
name: "nulo",
link: "#",
description:"nulo"
});
//Colection
Peliculas = Backbone.Collection.extend({
initialize: function (models, options) {
this.bind("add", options.view.addPeliculaLi);
this.bind("remove", options.view.delPeliculaLi);
}
});
//View
AppView = Backbone.View.extend({
el: $("body"),
initialize: function () {
this.peliculas = new Peliculas( null, { view: this });
//here I add a couple of models
this.peliculas.add([
{name: "Flying Dutchman", link:"#", description:"xxxxxxxxxxxx"},
{name: "Black Pearl", link: "#", description:"yyyyyyyyyyyyyy"}
])
},
events: {"click #add-movie":"addPelicula", "click .eliminar":"delPelicula"},
addPelicula: function () {
var pelicula_name = $("#movieName").val();
var pelicula_desc = $("#movieDesc").val();
var pelicula_model = new Pelicula({ name: pelicula_name },{ description: pelicula_desc });
this.peliculas.add( pelicula_model );
},
addPeliculaLi: function (model) {
var str= model.get('name').replace(/\s+/g, '');
elId = str.toLowerCase();
$("#movies-list").append("<li id="+ elId +"> " + model.get('name') + " <a class='eliminar' href='#'>Eliminar</a> </li>");
},
delPelicula: function (model) {
this.peliculas.remove();
console.log("now should be triggered the -delPeliculaLi- event bind in the collection")
},
delPeliculaLi: function (model) {
console.log(model.get('name'));
$("#movies-list").remove(elId);
}
});
var appview = new AppView;
})(jQuery);
And my html is:
<div id="addMovie">
<input id="movieName" type="text" value="Movie Name">
<input id="movieDesc" type="text" value="Movie Description">
<button id="add-movie">Add Movie</button>
</div>
<div id="lasMovies">
<ul id="movies-list"></ul>
</div>
There are several things in this code that won't work. Your major problem here is that you don't tell your collection which model to remove. So in your html you have to assign so unique id that later will identify your model.
// set cid as el id its unique in your collection and automatically generated by collection
addPeliculaLi: function (model) {
$("#movies-list").append("<li id="+ model.cid +"> <a href="+ model.get('link')+">" +
model.get('name') + "</a> <a class='eliminar' href='#'>Eliminar</a> </li>"
);
},
// fetch and delete the model by cid, the callback contains the jQuery delete event
delPelicula: function (event) {
var modelId = this.$(event.currentTarget).attr('id');
var model = this.peliculas.getByCid(modelId);
this.peliculas.remove(model);
// now the remove event should fire
},
// remove the li el fetched by id
delPeliculaLi: function (model) {
this.$('#' + model.cid).remove();
}
If there aren't other errors that I have overlooked your code should work now. This is just a quick fix. Maybe you should have a look at the todos example of Backbone to get some patterns how to structure your app.
http://documentcloud.github.com/backbone/examples/todos/index.html

Resources