Vue.js & Laravel - How to retrieve input for comment edit - laravel

I am new to Vue.js.
I am making a comment and reply system.
Creating and Deleting a comment and reply are working fine.
I want to edit a comment. Editing without a retrieving a current comment is working fine.
Only problem is I don't know how to retrieve a current comment on edit field.
I think I need to insert {{ comment.cotent }} into somewhere in textarea or insert getComment method on editComment method but I don't know how to do it.
I am using Laravel and Vue.js 2 and Vue-resource.
<!-- This is a text area for write a comment. This works fine. -->
<div class="" v-if="$root.user.authenticated">
<textarea placeholder="Write a comment here!" class="form-control" v-model="content"></textarea>
<div class="pull-right">
<button type="submit" class="btn btn-default" #click.prevent="createComment">Save</button>
</div>
</div>
<!-- Showing comments. This works fine. -->
<li class="media" v-for="comment in comments">
<p>{{ comment.content }}</p>
</li>
<!-- Editing a comment without retrieving a current comment. This works fine but I want to retrieve a current comment!!!! -->
<div class="" v-if="commentEditFormVisible === comment.id">
<textarea class="form-control comment__input" v-model="commentEditBody"></textarea>
<div class="pull-right">
<button type="submit" class="btn btn-default" #click.prevent="editComment(comment.id)">Edit</button>
</div>
</div>
<!-- script -->
data() {
return {
comments: [],
content: null,
replyBody: null,
replyFormVisible: null,
commentEditBody: null,
commentEditFormVisible: null,
}
},
getComments() {
this.$http.get('/blog/' + this.blogslug + '/comments').then((response) => {
this.comments = response.body.data;
});
},
editComment(commentId) {
this.$http.put('/blog/' + this.blogslug + '/comments/' + commentId, {
content: this.commentEditBody
}).then((response) => {
this.content = null;
});
},

Related

Vue.js 3 - Add list items from component dynamically

I am new to vue, I found some examples but I could not reproduce in my situation.
I have some inputs user will fill out and once it clicks the button, the information will populate a list of items. I am trying to crete < li > with a OrderListItem component. But I am getting this error:
runtime-core.esm-bundler.js?5c40:217 Uncaught ReferenceError: machines is not defined
at eval (OrderListItem.vue?fb66:14)
Here is the form
<template>
<div>
<div class="flex flex-col mb-4 md:w-full m-1">
<label for="planter_model_id">Planter</label>
<select v-model="registration.planter_model_id" name="planter_model_id">
<option disabled selected>Planter</option>
<option v-for="planter in planters" :key="planter" :value="planter.id">{{planter.brand.name}} {{planter.name }}</option>
</select>
</div>
<div class="flex flex-col mb-4 md:w-full m-1">
<label class=" for="lines">Lines</label>
<Input v-model="registration.lines" type="number" name="lines" />
</div>
<Button type="button" #click="addItemOrder">Add</Button>
</div>
<div>
<ul>
<OrderListItem :machines="machines" />
</ul>
<div>
</template>
Here is my code
export default {
components: {
OrderListItem,
Button,
},
props: {
planters:{
type:Array,
required: true
},
},
data() {
return {
machines: [], //this what I wish to be passed to OrderListItem component
registration:{
lines:null,
planter_model_id:null,
},
}
},
methods:{
addItemOrder(){
let item = {}
item.planter_model_id = this.registration.planter_model_id
item.lines = this.registration.lines
this.machines.push(item)
}
}
};
And here is my OrderListItem component that I created on another file:
<template>
<li v-for="machine in machines" :key="machine">
<div class="flex-initial w-3/5">
<p>Planter: {{machine.planter_model_id}}</p>
<p>Lines: {{machine.lines}}</p>
</div>
</li>
</template>
<script>
export default {
name: 'OrderListItem',
props:{
machines,
}
}
</script>
I don’t know if it is relevant but I am using vue3 and laravel.
I am very newbie here and from what I learned I think if machines array was a prop I would be able to access it on my component. But I will dynamically add a and remove itens from machines array and I think props receive data from server so It should not be declared there, that why I declared inside data().
Thanks =)
You should define the prop by adding its type and default value :
export default {
name: 'OrderListItem',
props:{
machines:{type:Array,default:()=>[]},
}
}

Knockout observable is not accessible inside function

So I am working with Knockout inside Magento 2.3.4, and I am setting a custom observable value on initialize, and then I am trying to access that observable and change the value inside a function. Every time I try, I keep getting "it is not a function", and it won't let me either retrieve and read the current observable value, or set a new one. When I try to run .isObservable() on it it comes up as false. I have looked through various examples of how to do it and tried all of them and none of them work. Currently my knockout JS form looks like this:
define([
'jquery',
'uiComponent',
'ko'
], function($, Component, ko) {
'use strict';
return Component.extend({
defaults: {
template: 'Shmoop_Cms/career-form'
},
progressText: ko.observable(false),
initialize: function() {
var self = this;
this._super();
this.progressText('1 of 15 questions completed');
return this;
},
showNext: function() {
let dataIndex = parseInt($('.quiz-card.show').attr('data-index')) + 1;
alert(ko.isObservable(this.progressText));
alert(this.progressText());
this.progressText(dataIndex + ' of 15 questions completed');
}
});
});
I am able to set the progressText value initially inside that initialize function without issue, and it recognizes there that it is an observable. Why does it say it's not an observable inside by "showNext" function?
FYI I have also tried adding "var self = this" inside my function, I have also tried "self.progressText()" instead of "this.progressText()", nothing worked.
Please help.
Edited: By the way, my template looks like this:
<div class="career-quiz-wrapper">
<!-- ko if: quizQuestions -->
<form class="career-quiz-form" data-bind="foreach: quizQuestions">
<div class="quiz-card" data-bind="attr: {'data-question-uuid': uuid, 'data-index': index }, css: index == 0 ? 'show' : 'hide'">
<h2 class="display-heading title">How important is it to you to...</h2>
<div class="lead main-text" data-bind="html: question"></div>
<div class="choices">
<div class="row-bar">
<input type="range" data-anchor="range" class="custom-range" min="0" max="100" value="50" autocomplete="off" data-bind="event: { change: $parent.adjustRangeSlider }">
</div>
<div class="row-options">
<div class="option-section" data-bind="foreach: choiceUUIDs">
<div class="choice-option-button">
<a class="option-button" data-bind="click: $parents[1].choiceClicked, attr: { 'data-choice-uuid': UUID, 'data-range-value': rangeValue, title: textOption }, text: textOption, css: (i && i == 2) ? 'selected' : ''"></a>
</div>
</div>
<div class="mobile-bar">
<input type="range" data-anchor="range" class="custom-range" min="0" max="100" value="50" autocomplete="off" data-bind="event: { change: $parent.adjustRangeSlider }">
</div>
</div>
</div>
<div class="choice-buttons">
<div class="choice-button">
<a data-bind="click: $parent.showCareers" class="blue-link" title="Show Careers">Show Careers</a>
</div>
<!-- ko if: index == ($parent.quizQuestions().length - 1) -->
<div class="choice-button">
<a class="pink-button results-button" data-bind="click: $parent.getResults" title="Get Results">Get Results <i class="fa fa-cog fa-spin d-none"></i></a>
</div>
<!-- /ko -->
<!-- ko ifnot: index == ($parent.quizQuestions().length - 1) -->
<div class="choice-button">
<a class="pink-button next-question" data-bind="click: $parent.showNext" title="Next Question">Next Question</a>
</div>
<!-- /ko -->
</div>
<div class="quiz-progress">
<p class="progress-text" data-bind="text: $parent.progressText"></p>
</div>
</div>
</form>
<!-- /ko -->
<!-- ko ifnot: quizQuestions -->
<div class="error-message">
<p>
Sorry, something went wrong. I guess you'll have to figure out what to do on your own..
</p>
</div>
<!-- /ko -->
I have had issues with javascript object literals before and would probably approach it like this. Not sure if this works as I haven't tested it.
define([
'jquery',
'uiComponent',
'ko'
], function($, Component, ko) {
'use strict';
var self = this;
self.progressText = ko.observable(false);
function initialize() {
this._super();
self.progressText('1 of 15 questions completed');
return this;
}
function showNext() {
let dataIndex = parseInt($('.quiz-card.show').attr('data-index')) + 1;
alert(ko.isObservable(self.progressText));
alert(self.progressText());
self.progressText(dataIndex + ' of 15 questions completed');
}
return Component.extend({
defaults: {
template: 'Shmoop_Cms/career-form'
},
progressText: self.progressText,
initialize: initialize,
showNext: showNext
});
});

How to POST an object to controller

I'm having a difficulty passing my 'product' object to the controller. How can I do it? I'm not getting errors. The problem is that the 'product' object is null on my controller.
html:
<section th:each="menu : ${allMenus}">
<button
<h1 th:text="${menu.name}"></h1>
</button>
<div>
<div th:each="product : ${menu.productList}">
<a data-toggle="modal" th:href="'#' + ${product.name} + 'Modal'">
h5 th:text="${product.name}"></h5>
<small th:text="${product.price} + '$'"></small>
<p th:text="${product.description}"></p>
</a>
<div th:replace="/productModal :: productModal(product=${product})"></div>
</div>
</section>
Modal:
<div th:fragment="productModal(product)">
<div role="document">
<form method="post" th:action="#{/addItemToCart}">
<div th:each="topping : ${product.toppings}">
<input type="checkbox" th:id="${topping} + ${product.id}" name="checkedToppings" th:value="${topping}" />
<label th:for="${topping} + ${product.id}" th:text="${topping}"></label>
</div>
<div>
<button type="submit">Add to Shopping Cart</button>
</div>
</form>
</div>
</div>
controller:
#RequestMapping(value="/addItemToCart", method=RequestMethod.POST)
public String addItemToCart(#ModelAttribute("product") Product product, #RequestParam("checkedToppings") List<String> toppings)
{
//product is null;
//checkedToppings are retrieved correctly
return "redirect:/menu";
}
Short answer:
you don't post objects to controllers using HTML.
Details:
You will never be able to post a "product" object to your controller from an HTML page.
Instead,
you should send identifying information about the desired "product" to the controller,
perhaps a product-id or some other product-unique-identity-blammy.
Response to options in comments:
Hackers love hidden fields and JavaScript;
I recommend against using those for this situation.
I believe that you only have one option: identifying info.
This does not need to be a "real" product number.
You can generate a UUID and store a map in the choose one: (Servlet Session, Database, Application Session, somewhere else on the server) that maps from the UUID to the desired product.

Passing the Div id to another vue component in laravel

I created a simple real-time chat application using vue js in laravel.
I am having a problem with the automatic scroll of the div when there is a new data.
What I want is the div to automatically scroll down to the bottom of the div when there is a new data.
Here is my code so far.
Chat.vue file
<template>
<div class="panel-block">
<div class="chat" v-if="chats.length != 0" style="height: 400px;" id="myDiv">
<div v-for="chat in chats" style="overflow: auto;" >
<div class="chat-right" v-if="chat.user_id == userid">
{{ chat.chat }}
</div>
<div class="chat-left" v-else>
{{ chat.chat}}
</div>
</div>
</div>
<div v-else class="no-message">
<br><br><br><br><br>
There are no messages
</div>
<chat-composer v-bind:userid="userid" v-bind:chats="chats" v-bind:adminid="adminid"></chat-composer>
</div>
</template>
<script>
export default {
props: ['chats','userid','adminid'],
}
</script>
ChatComposer.vue file
<template>
<div class="panel-block field">
<div class="input-group">
<input type="text" class="form-control" v-on:keyup.enter="sendChat" v-model="chat">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" v-on:click="sendChat">Send Chat</button>
</span>
</div>
</div>
</template>
<script>
export default{
props: ['chats','userid','adminid'],
data() {
return{
chat: ''
}
},
methods: {
sendChat: function(e) {
if(this.chat != ''){
var data = {
chat: this.chat,
admin_id: this.adminid,
user_id: this.userid
}
this.chat = '';
axios.post('/chat/sendChat', data).then((response) => {
this.chats.push(data)
})
this.scrollToEnd();
}
},
scrollToEnd: function() {
var container = this.$el.querySelector("#myDiv");
container.scrollTop = container.scrollHeight;
}
}
}
</script>
I am passing a div id from the Chat.vue file to the ChatComposer.vue file.
As you can see in the ChatComposer.vue file there is a function called scrollToEnd where in it gets the height of the div id from Chat.vue file.
When the sendchat function is triggered i called the scrollToEnd function.
I guess hes not getting the value from the div id because I am getting an error - Cannot read property 'scrollHeight' of null.
Any help would be appreciated.
Thanks in advance.
the scope of this.$el.querySelector will be limited to only ChatComposer.vue hence child component can not able to access div of parent component #myDiv .
You can trigger event as below in ChatComposer
this.$emit('scroll');
In parent component write ScollToEnd method and use $ref to assign new height
<chat-composer v-bind:userid="userid" v-bind:chats="chats" v-bind:adminid="adminid" #scroll="ScollToEnd"></chat-composer>
..

angularUI select dropdown displays data only after entering a character

I am using AngularJS v1.2.15 and angular-ui / ui-select. My select HTML is:
<div class="form-group col-md-3">
<div class="input-group select2-bootstrap-append">
<ui-select ng-model="modelOwner.selected" theme="select2" class="form-control">
<match placeholder="Select Owner">{{$select.selected.name}}</match>
<choices repeat="item in owner | filter: $select.search">
<span ng-bind-html="item.name | highlight: $select.search"></span>
</choices>
</ui-select>
<span class="input-group-btn">
<button ng-click="modelOwner.selected = undefined" class="btn btn-danger">
<span class="glyphicon glyphicon-trash"></span>
</button>
</span>
</div>
</div>
My call in controller is:
$scope.modelOwner = {};
OwnersFactory.query({}, function (data) {
$scope.owner = data;
});
My service code:
bootstrapApp.factory('OwnersFactory', function ($http,$state,serviceUrl,$resource,$log) {
return $resource(serviceUrl + 'owner/:id', {}, {
show: { method: 'GET', params: {}, isArray: false }
})
});
Now, in my form i can view the values only after entering at least a single character. I want this select dropdown to display values just by clicking on the dropdown (not by entering any character.)
Possible Solution: if i could load my state only after all the AJAX calls have been made.
Please help me out here.
if you are using ui-router you can use resolve on each state, so that the call is resolved before initializing the controller
https://github.com/angular-ui/ui-router/wiki#resolve

Resources