While attempting to copy and improve upon the todo list in the front page of canjs.org I have run into a bit of a snag. The example doesn't show you how to add items to a todo list. So I added a "Add another" span that executes newTodo when clicked.
<h2>Todays to-dos</h2>
{{#selectedTodo}}
<input type="text" can-value="description" can-change="saveTodo">
{{/selectedTodo}}
<ul>
{{#each todos}}
<li>
<input type="checkbox" can-value="complete" can-click="saveTodo">
<span class="description {{#if complete}}done{{/if}}" can-click="select">{{description}}</span>
<button can-click="destroy"><i class="fa fa-close"></i></button>
</li>
{{/each}}
<li>
<span class="description" can-click="newTodo">Add Another</span>
</li>
</ul>
Next I add the newTodo function which resets the list of Todos after saving the new one.
var Todo = can.Model.extend({
findAll: 'GET /todo/',
findOne: 'GET /todo/{id}/',
update: 'POST /todo/{id}/',
destroy: 'POST /todo/delete/{id}/',
create: 'POST /todo/new/',
}, {});
can.Component.extend({
tag: 'todolist',
template: can.view("/static/can/todo_list.html"),
scope: {
selectedTodo: null,
todos: new Todo.List({}),
select: function(todo){
this.attr('selectedTodo', todo);
},
saveTodo: function(todo) {
todo.save();
this.removeAttr('selectedTodo');
},
newTodo: function() {
var that = this;
var t = new Todo({}).save(function() { that.attr("todos",new Todo.List({})) });
},
}
})
$(function() {
$("#todo-wrapper").append(can.mustache("<todolist></todolist>")({}));
});
However, this causes the list to be wiped entirely and then rewritten, causing an ugly blinking effect. I feel like there's a better way to do this.
Related
I'm making a discussion forum with comments and replies, I used to display them in a table but I've changed and am now using cards to display them, like so:
This was I can display correctly the replies, however now I'm not sure how I would delete or edit the comments the user has made, or reply to other comments. To note I'm not displaying the reply button but there would be one on the parent comment, I'm not sure how I would write the logic for a reply to a reply.
This is the functional vue.js code for deleting a table row, editing a table row and replying. I'm using dialogs to get the user input.
openReply(row) {
this.dialogReplyVisible = true;
this.parent = row;
},
edit(model) {
this.mode = 'Editar';
this.form = _.cloneDeep(model);
this.dialogFormVisible = true;
},
remove(row) {
this.$confirm('Desea borrar este Comentario? Esto es permanente.',
'Advertencia', {
confirmButtonText: 'Si',
cancelButtonText: 'Cancelar',
cancelButtonClass: 'el-button--info',
confirmButtonClass: 'el-button--warning',
type: 'warning'
}).then(() => {
this.loading = true;
this.$inertia.delete(this.baseUrl + '/' + row.id)
.then(
() => {
this.$message({
type: 'success',
message: 'Eliminado correctamente.'
});
this.comments = this.$page.comments;
this.loading = false
},
(res) => {
this.$message.error(parseError(res)[0]);
this.loading = false;
}
)
})
},
The routes on the web.php
Route::post('comments/update/{id}', 'ReplyController#update');
Route::post('comments/reply', 'ReplyController#replyStore');
Route::post('comments/reply/update/{id}', 'ReplyController#replyUpdate');
Route::resource('comments', 'ReplyController'); //this does the store and delete
These are the buttons where these methods get called
<div class="btn-link-edit action-button"
#click="edit(scope.row)">
<i class="fas fa-pencil-alt"></i>
</div>
<div class="btn-link-delete action-button"
#click="remove(scope.row)">
<i class="fas fa-trash"></i>
</div>
<div class="btn-link-preview action-button"
#click="openReply(scope.row)">
<i class="fas fa-reply"></i>
</div>
Other relevant data is the form its trying to clone and the base urls
form: {
comment: '',
},
replyForm: {
comment: '',
},
baseUrl: '/comments',
customUpdateUrl: '/comments/update',
So my final question is how to change those edit, delete and reply functions to get it to work with cards instead of a table.
EDIT: I got the remove function working
The code itself is still good I just needed to change with I sent though as the parameter. I didn't know that the model it was asking for is basically just an object, so I just send through the object I wanted for the edit and reply and for the remove I just passed the id through and changed the post url from
this.$inertia.delete(this.baseUrl + '/' + row.id)
to
this.$inertia.delete(this.baseUrl + '/' + id)
I could've just sent the object through as well, as it would've taken the id when sending it anyway.
The button with the new parameters
<div class="btn-link-preview action-button"
#click="openReply(comment)">
<i class="fas fa-reply"></i>
</div>
<div class="btn-link-edit action-button"
#click="edit(comment)">
<i class="fas fa-pencil-alt"></i>
</div>
<div class="btn-link-delete action-button"
#click="remove(comment.id)">
<i class="fas fa-trash"></i>
</div>
This is what is inside comment without replies, and the object I send though
{
"id":8,
"user_id":24,
"discussion_forum_id":1,
"parent_id":null,
"comment":"adsfadsfasdf",
"comment_time":"2019-12-03 14:55:09",
"created_at":null,
"updated_at":null,
"user":{
"id":24,
"name":"Vinny Ridley",
"card":"12353",
"scard":"97524",
"user_type_id":4,
"email":"v#email.com",
"created_at":"2019-12-02 13:07:37",
"updated_at":"2019-12-02 13:07:37"
},
"replies":[
]
}
For editing and deleting a reply I just send reply or reply.id and that does it. Hope this helps someone with the same or similar issue.
My page exist of a table where I can add new rows. If you want to add a new row a pop-up window appear where the new values can be added.
This new data is then saved to the database after submitting. If I again want to add a new row the input fields, they should be cleared.
The method I use, is working but isn't very clear.
Note: My code shows only a part of the input fields, to make it more clear. My pop-up window actually contains 20 input fields.
I would like to clear them all at once instead of clearing them one by one (like I am doing now).
Because I am already doing this for defining the v-model, pushing the new data to the database directly on the page and via post axios request.
Is there a cleaner way to do this?
Thanks for any input you could give me.
This is my code:
html part
<div class="col-2 md-2">
<button class="btn btn-success btn-sx" #click="showModal('add')">Add New</button>
<b-modal :ref="'add'" hide-footer title="Add new" size="lg">
<div class="row" >
<div class="col-4">
<b-form-group label="Category">
<b-form-input type="text" v-model="newCategory"></b-form-input>
</b-form-group>
</div>
<div class="col-4">
<b-form-group label="Name">
<b-form-input type="text" v-model="newName" placeholder="cd4"></b-form-input>
</b-form-group>
</div>
<div class="col-4">
<b-form-group label="Amount">
<b-form-input type="number" v-model="newAmount" ></b-form-input>
</b-form-group>
</div>
</div>
<div class="row" >
<div class="col-8">
</div>
<div class="col-4">
<div class="mt-2">
<b-button #click="hideModal('add')">Close</b-button>
<b-button #click="storeAntibody(antibodies.item)" variant="success">Save New Antibody</b-button>
</div>
</div>
</div>
</b-modal>
</div>
js part
<script>
import { async } from 'q';
export default {
props: ['speciedata'],
data() {
return {
species: this.speciedata,
newCategory: '',
newName: '',
newAmount:'',
}
},
computed: {
},
mounted () {
},
methods: {
showModal: function() {
this.$refs["add"].show()
},
hideModal: function(id, expId) {
this.$refs['add'].hide()
},
addRow: function(){
this.species.push({
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
},
storeSpecie: async function() {
axios.post('/specie/store', {
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
.then(this.addRow())
// Clear input
.then(
this.newName = '',
this.newCategory = '',
this.newAmount = '',
)
.then(this.hideModal('add'))
},
}
}
</script>
in your data of vuejs app , you have to set one object for displaying modal data like modalData then to reset data you can create one function and set default value by checking type of value using loop through modalData object keys
var app = new Vue({
el: '#app',
data: {
message:"Hi there",
modalData:{
key1:"value1",
key2:"value2",
key3:"value3",
key4:5,
key5:true,
key6:"val6"
}
},
methods: {
resetModalData: function(){
let stringDefault="";
let numberDefault=0;
let booleanDefault=false;
Object.keys(this.modalData).forEach(key => {
if(typeof(this.modalData[key])==="number"){
this.modalData[key]=numberDefault;
}else if(typeof(this.modalData[key])==="boolean") {
this.modalData[key]=booleanDefault;
}else{
// default type string
this.modalData[key]=stringDefault;
}
});
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
{{modalData}}
<br/>
<button #click="resetModalData">Reset Modal Data</button>
</div>
update : in your case :
data:{
species: this.speciedata,
modalData:{
newCategory: '',
newName: '',
newAmount:''
}
},
and after storing data :
storeSpecie: async function() {
axios.post('/specie/store', {
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
.then(()=>{
this.addRow();
this.resetModalData();
this.hideModal('add')
}
},
In native Javascript you get the reset() method.
Here is how it is used :
document.getElementById("myForm").reset();
It will clear every input in the form.
I'm new to Vue.js, trying to create a single page blog just to get my feet wet with the vue/laravel combo, and I am stuck when it comes to deleting a "story" from the array of "stories" I am working with. I know the routes are fine because the story actually deletes, no errors are thrown, but the deleted story will remain in the array until I refresh the page. From what I've read elsewhere, the code I have implemented should update the array immediately. I have attached the relevant parts of my blade view, vue.js file, and controller. Thanks in advance!
JS (VUE)
new Vue({
el: '[vue-news]',
search: "",
data: {
stories: ''
},
ready: function() {
// GET request
this.$http.get('/app/news/get', function(data, status, request) {
// set data on vm
this.$set('stories', data);
// console.log(data);
}).error(function(data, status, request) {
// handle error
});
},
methods: {
// DELETE STORY FROM ARRAY
deleteNews: function(id, index) {
this.$http.delete('app/news/' + id + '/delete').success(function(response) {
this.stories.$remove(index);
swal("Deleted!", "Your news story has been deleted.", "success");
}).error(function(error) {
console.log(error);
swal(error);
});
}
}
});
BLADE
<section vue-news>
<div class="row news-row">
<div class="columns large-9 medium-9 small-12">
<article data-id="#{{ story.id }}" class="story panel" v-for="story in stories | filterBy search" track-by="$index">
<h1>#{{ story.title }}</h1>
<p>#{{ story.content }}</p>
<p>#{{ story.created_at }}</p>
<p>#{{ story.id }}</p>
<p>#{{ story.timestamp }}</p>
Read More...
<div class="options">
<a #click="editNews" href="#">
<i class=" edit fa fa-pencil-square-o"></i>
</a>
{{-- DELETE NEWS BUTTON --}}
<a #click.prevent="deleteNews(story.id, $index)" href="#">
<i class="delete fa fa-trash-o"></i>
</a>
</div>
</article>
</div>
<div class="columns large-3 medium-3 small-12">
<input type="text" v-model="search">
</div>
</div>
</section>
CONTROLLER
public function delete($id)
{
return response()->json(News::destroy($id));
}
The $remove method now treats the argument as an item to search for rather than an index. In other words, try this out:
Delete method:
deleteNews: function(id, story) {
this.$http.delete('app/news/' + id + '/delete').success(function(response) {
this.stories.$remove(story);
swal("Deleted!", "Your news story has been deleted.", "success");
}).error(function(error) {
console.log(error);
swal(error);
});
}
HTML section:
<a #click.prevent="deleteNews(story.id, story)" href="#">
<i class="delete fa fa-trash-o"></i>
</a>
Source: https://github.com/vuejs/vue/releases
Edit: Since you are passing the entire story item, you can actually just pass one argument and shorten the code to this:
Vue:
deleteNews: function(story) {
this.$http.delete('app/news/' + story.id + '/delete').success(function(response) {
this.stories.$remove(story);
swal("Deleted!", "Your news story has been deleted.", "success");
}).error(function(error) {
console.log(error);
swal(error);
});
}
HTML:
<a #click.prevent="deleteNews(story)" href="#">
<i class="delete fa fa-trash-o"></i>
</a>
I have my edit form with all of my information load in it, and when I click on the save button, it take all of field to save even if there is nothing changed in it..
I wanted to get back in an object only the index and their value that I modified in the form.
I have stricly no idea how I can do this with Ember.
Here is my code :
App.EnquiryUpdateController = Ember.ObjectController.extend({
id: null,
isSaved: false,
actions: {
save: function() {
var enquiry = this.get('model');
console.log(enquiry);
var obj = JSON.parse(JSON.stringify(enquiry));
obj = this.cleanObject(obj);
$.ajax({
url: host + 'mdf/enquiry/' + enquiry.id,
type: 'POST',
accepts: 'application/json',
data: obj
});
this.transitionToRoute('enquiry', enquiry)
}
},
});
My Route:
App.EnquiryUpdateRoute = Ember.Route.extend({
model: function() {
return this.modelFor('enquiry');
}
});
And my Template :
<script type="text/x-handlebars" data-template-name="enquiry/update">
<div class="enquiry-update">
{{#if isSaved}}
<div class="saved">Enquiry updated successfully</div>
{{/if}}
<div>
<label>Customer Name</label>
{{input value=customerName}}
</div>
<div>
<label>Customer Email</label>
{{input value=customerEmail}}
</div>
<div>
<label>Customer Phone</label>
{{input value=customerPhone}}
</div>
<div>
<button {{action "save"}}>Save</button>
</div>
</div>
</script>
If I update the customerName, I only want to have it in my object { customerName: "toto" } instead of all of them..
Thanks !
Well, the concept you're going to want to implement will be dirty attribute checking. This means you'll need to keep a copy of the original data and then either mark an attribute as dirty, or compare attributes and only send the different attributes.
I have a very simply page at the moment. It has a first name input, last name input, and a list of names added. You can add your first and last name to the text box, press add. It adds it the peopleList I have and adds a new listItem with their name.
My issue is when I add to the peopleList in my code, it does not update the listView. I think I need to use observable, but I am not exactly sure how to do it. My list shows it has 25 items added to it after I click btnMany, which is how many it show have.
here is the body of my code:
<!--Load Some Data-->
<div id="peopleDefaultView"
data-role="view"
data-model="ViewModel"
data-layout="default">
<!--View-->
<input type="text" data-bind="value: firstName" id="fName" />
<input type="text" data-bind="value: lastName" id="lName" />
<a data-bind="click: addName" data-role="button" id="btnOne">Add</a>
<a data-bind="click: setValues" data-role="button" id="btnMany">Add Many</a>
<div style="margin-top: 10px">
People List:
<ul data-template="people-l-template" data-bind="source: peopleList" id="pList"></ul>
</div>
</div>
<!-- Kendo Template-->
<script id="people-l-template" type="text/x-kendo-template">
<li>
FirstName: <span data-bind="text: first"></span>
LastName: <span data-bind="text: last"></span>
<a data-bind="click: removeName" data-role="button" id="btnRemoveName">X</a>
</li>
</script>
And here is my script to go along with it
<script>
var ViewModel = {
firstName: '',
lastName: '',
peopleList: [],
addName: function (e) {
this.get("peopleList").push({
first: this.get("firstName"),
last: this.get("lastName"),
});
this.set("firstName", '');
this.set("lastName", '');
},
removeName: function (e) {
this.set("peopleList", jQuery.grep(this.peopleList, function (item, i) {
return (item.firstName != e.data.firstName && item.lastName != e.data.lastName);
}));
},
setValues: function (e) {
GetValueFromServer();
}
};
var GetValueFromServer = function () {
$.ajax({
type: "GET",
url: "GetPeopleService.svc/GetPersonById/",
data: {},
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
response.forEach(function (person) {
ViewModel["peopleList"].push({
first: person.firstName,
last: person.lastName
});
});
alert(ViewModel.peopleList.length);
},
error: function (response) {
console.log(response);
}
});
};
var application = new kendo.mobile.Application(document.body);
</script>
There were a few things wrong with the code you provided, the most notably being that you didn't set the role for your <ul> element. You need to change it to have the attribute data-role="listview". You also can't use an <li> element as the root element for a listview template (KendoUI automatically takes care of this for you), otherwise you'll get an error when the list is bound.
Here's an example on JS Bin.
And here's the code:
<!--Load Some Data-->
<div id="peopleDefaultView"
data-role="view"
data-model="viewModel"
data-layout="flat">
<!--View-->
<input type="text" data-bind="value: firstName" id="fName" />
<input type="text" data-bind="value: lastName" id="lName" />
<a data-bind="click: addName" data-role="button" id="btnOne">Add</a>
<div style="margin-top: 10px">
People List:
<ul id="pList"
data-role="listview"
data-template="people-l-template"
data-bind="source: peopleList">
</ul>
</div>
</div>
<!-- Kendo Template-->
<script id="people-l-template" type="text/x-kendo-template">
FirstName: <span>#:first#</span>
LastName: <span>#:last#</span>
<a id="btnRemoveName"
data-role="button"
data-bind="click: removeName"
data-first="#:first#" data-last="#:last#">
X
</a>
</script>
...
var viewModel = {
firstName: null,
lastName: null,
peopleList: [],
addName: function (e) {
var me = this;
me.get('peopleList').push({
first: me.get('firstName'),
last: me.get('lastName')
});
me.set('firstName', '');
me.set('lastName', '');
},
removeName: function (e) {
var me = this;
me.set('peopleList', $.grep(me.peopleList, function (item, i) {
return item.first != e.target.data('first')
&& item.last != e.target.data('last');
}));
}
};
var application = new kendo.mobile.Application(document.body);