Vue js retrive new data after ajax call - javascript

I have one button when I click on this button I retrive data. But when I click second way I dont retrive new data
Here is my code
https://codepen.io/anon/pen/weobMP
Html
<button type="submit" class="btn btn-sm btn-success form-control" id="click">Filter</button>
<div class="col-md-6 cart-items" id="myjp">
<div v-for="(item, index) in items">
<div class="panel panel-info">
<div class="panel-heading" v-on:click="greet">
<h3 {{index+1}} {{item.Car}}</h3>
</div>
<div class="mojbody panel-body" v-for="tire in item.tires">
Tire: {{tire.Name}}
</div>
</div>
</div>
</div>
JS
$(function() {
var id = 1;
$("#click").click(function(){
id++;
new Vue({
el: '#myjp',
data: {
items: null
},
created: function () {
this.fetchData();
},
methods: {
fetchData: function () {
var self = this;
$.get('/retriveHistory',{id: id}, function( data ) {
self.items = data;
console.log(data);
});
},
}
});
})
});

Related

Updating module on instant change when list is updated

Sorry for the long post, but I tried explaining things in as much detail as possible.
So as I dive deeper into JavaScript and start learning more and more about AJAX requests and other components, I've stumbled across something that I can't seem to figure out.
So below, I will explain what I'm doing and what I would like to do, and see if someone has some guidance for me.
So here is my Vue.js app:
new Vue({
name: 'o365-edit-modal',
el: '#o365-modal-edit',
data: function() {
return {
list: {},
}
},
created() {
this.fetchApplicationsMenu();
},
methods: {
fetchApplicationsMenu() {
var self = this;
wp.apiRequest( {
path: 'fh/v1/menus/applications',
method: 'GET',
}).then(menu => self.list = menu.data);
},
changed() {
const selected = this.$data.list.selected;
function get_ids(list, field) {
const output = [];
for (let i=0; i < list.length ; ++i)
output.push(list[i][field]);
return output;
}
const result = get_ids(selected, "id");
wp.apiRequest( {
path: 'fh/v1/menus/applications',
method: 'PUT',
data: {
ids: result,
},
}).then((post) => {
return post;
},
(error) => {
console.log(error);
});
},
add(x) {
this.$data.list.selected.push(...this.$data.list.available.splice(x, 1));
this.changed();
},
remove(x) {
this.$data.list.available.push(...this.$data.list.selected.splice(x, 1));
this.changed();
},
},
});
Then here is the HTML portion that I'm using to render the two columns:
<div class="column is-half-desktop is-full-mobile buttons">
<nav class="level is-mobile mb-0">
<div class="level-left">
<div class="level-item is-size-5 has-text-left">Selected</div>
</div>
<div class="level-right">
<div class="level-item">
<i class="fas fa-sort-alpha-up is-clickable"></i>
</div>
</div>
</nav>
<hr class="mt-1 mb-3">
<draggable class="list-group"
v-model="list.selected"
v-bind="dragOptions"
:list="list.selected"
:move="onMove"
#change="changed">
<button class="button is-fullwidth is-flex list-group-item o365_app_handle level is-mobile" v-for="(app, index) in list.selected" :key="app.id">
<div class="level-left">
<span class="icon" aria-hidden="true">
<img :src="app.icon_url" />
</span>
<span>{{app.name}}</span>
</div>
<div class="level-right">
<span class="icon has-text-danger is-clickable" #click="remove(index)">
<i class="fas fa-times"></i>
</span>
</div>
</button>
</draggable>
</div>
<div class="column is-half-desktop is-full-mobile buttons">
<div class="is-size-5 has-text-left">Available</div>
<hr class="mt-1 mb-3">
<draggable class="list-group"
v-model="list.available"
v-bind="dragOptions"
:list="list.available"
:move="onMove">
<button class="button is-fullwidth is-flex list-group-item o365_app_handle level is-mobile" v-for="(app, index) in list.available" :key="app.id">
<div class="level-left">
<span class="icon" aria-hidden="true">
<img :src="app.icon_url" />
</span>
<span>{{app.name}}</span>
</div>
<div class="level-right">
<span class="icon has-text-primary is-clickable" #click="add(index)">
<i class="fas fa-plus"></i>
</span>
</div>
</button>
</draggable>
</div>
That outputs the following items, and all works great. See the video display below of each component working as needed. This all works great! I'm calling the changed() method on add and remove which grabs all the IDs and stores them in the DB via an endpoint.
The Problem:
Now I have the following dropdown menu, which depends on the fh/v1/menus/applications endpoint to pull in all the items as shown below:
As you can see below, when I open the dropdown, it has three apps, when I open the cog wheel and remove one of the apps and it saves it but the dropdown doesn't get automatically updated, I have to refresh the page and then I will see the updates.
Does anyone know how to fetch the new items without a refresh?
Here is the HTML and the JS for the dropdown piece:
HTML: As you can see in there, I have data-source="applications" which pulls in the items inside the init_menu as shown in the JS.
<div class="dropdown-menu" id="dropdown-o365" role="menu">
<div class="dropdown-content">
<div class="container is-fluid px-4 pb-4">
<?php if ($application = Applications::init()): ?>
<div class="columns">
<div class="dropdown-item column is-full has-text-centered is-size-6">
<div class="level is-mobile">
<div class="level-left">
<?= $application->get_name() ?>
</div>
<div class="level-right">
<a class="navbar-item modal-element icon" id="o365-apps-cogwheel" data-target="o365-modal-edit" aria-haspopup="true">
<i class="fa fa-cog"></i>
</a>
</div>
</div>
</div>
</div>
<div class="columns is-multiline" data-source="applications"></div>
<?php else: ?>
<div class="columns">
<div class="column is-full">
No applications present.
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
Then here is the JavaScript. I initilize the method inside DOMContentLoaded using init_menu('applications');:
function init_menu(paths)
{
paths.forEach(path => {
const target = document.querySelector('[data-source=' + path + ']');
if (target) {
wp.api.loadPromise.done(function () {
const Menus = wp.api.models.Post.extend({
url: wpApiSettings.root + 'fh/v1/menus/' + path,
});
const menus = new Menus();
menus.fetch().then(posts => {
// This returns the data object.
const data = posts.data;
let post_list;
// Check if it's an array and see if selected is empty otherwise show available.
if (Array.isArray(data.selected) && data.selected.length !== 0) {
post_list = data.selected;
} else {
post_list = data.available;
}
post_list.forEach(function (post) {
switch(path) {
case 'applications':
target.appendChild(create_apps_dom_tree(post));
break;
default:
console.log('Path route is invalid.');
break;
}
})
})
})
}
});
}
function create_apps_dom_tree(post) {
const {
icon_url,
url,
name,
} = post
const container = document.createElement('div');
container.className = 'column is-one-third is-flex py-0';
const anchor = document.createElement('a');
anchor.href = url;
anchor.className = 'dropdown-item px-2 is-flex is-align-items-center';
const figure = document.createElement('figure');
figure.className = 'image is-32x32 is-flex';
const img = document.createElement('img');
img.src = icon_url;
const span = document.createElement('span');
span.className = 'pl-2';
span.textContent = name;
figure.appendChild(img);
anchor.append(figure, span);
container.appendChild(anchor);
return container;
}
If anyone has some guidance or an answer on how to pull in live data from the database on the fly, that would be amazing.
Basically, I need my data-source: to automatically grab the items when my vue/db request is sent so I don't have to refresh the page.
Inside my Vue app, I have the following method:
fetchApplicationsMenu() {
var self = this;
wp.apiRequest( {
path: 'fh/v1/menus/applications',
method: 'GET',
}).then(menu => self.list = menu.data);
},
which calls a GET request and then stores the data inside the return { list: {} }.
A quick fix might be to just invoke init_menu() from the component's beforeDestroy() hook, called when the dialog closes. You might choose to do it from changed() instead if the dropdown is still accessible with this dialog open.
new Vue({
// option 1:
beforeDestroy() {
init_menu('applications');
},
// option 2:
methods: {
changed() {
init_menu('applications');
}
}
})
Alternative: You already know what the final application list is in changed(), so you could update the dropdown with the new list from that method.
function update_menu(path, post_list) {
const target = document.querySelector('[data-source=' + path + ']');
// remove all existing children
Array.from(target.childNodes).forEach(x => x.remove());
post_list.forEach(post => target.appendChild(create_apps_dom_tree(post)))
}
new Vue({
methods: {
changed() {
update_menu('applications', this.$data.available);
}
}
})

Vue JS dynamic modal with components

in news.twig
{% extends 'layouts.twig' %}
{% block content %}
<section class="ls section_padding_bottom_110">
<div id="cart" class="container">
<cart
v-bind:materials="news"
type="news"
test="materials"
></cart>
<modal></modal>
</div>
<script type="text/x-template" id="modal-template">
<transition name="modal">
<div class="modal-mask" v-if="active" #click="close()">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<h3>${ item.name }</h3>
</div>
<div class="modal-body">
${ item.body }
<br>
modal #${ item.id }
</div>
<div class="modal-footer">
<button class="modal-default-button" #click="close()">
close
</button>
</div>
</div>
</div>
</div>
</transition>
</script>
</section>
{% endblock %}
I have 2 components and 1 Vue in my js.
var Hub = new Vue();
Vue.component(
'modal', {
template: '#modal-template',
delimiters: ['${', '}'],
data: function() {
return {
active: false,
item: {
id: '',
name: '',
body: ''
}
}
},
methods: {
open: function (item) {
this.active = true;
this.item = item;
},
close: function () {
this.active = false;
}
},
mounted: function() {
this.$nextTick(function () {
Hub.$on('open-modal', this.open);
Hub.$on('close-modal', this.close);
}.bind(this));
}
});
Vue.component('cart', {
props: {
materials: { type: Array, required: true},
type: { type: String, required: true}
},
computed: {
isPoints() {
return this.type == 'paymentPoints';
},
isNews() {
return this.type == 'news';
}
},
template : `
<div class="row masonry-layout isotope_container">
<div class="col-md-4 col-sm-6 isotope-item" v-for="item in materials">
<div class="vertical-item content-padding topmargin_80">
<div class="item-media">
<img v-bind:src="item.image" alt="">
<div class="media-links p-link">
<div class="links-wrap">
<i class="flaticon-arrows-2"></i>
</div>
<a v-if="!isNews" v-bind:href="item.image" class="abs-link"></a>
</div>
</div>
<button #click="openModal(item)" #keyup.esc="closeModal()">more</button>
<div class="item-content with_shadow with_bottom_border">
<span v-if="isNews" class="categories-links" style="font-size:20px;">
<a rel="category" href="#modal1" data-toggle="modal">
{{item.name}}
</a>
</span>
<p>{{item.body}}</p>
<div v-if="isPoints">
<hr class="light-divider full-content-divider bottommargin_10">
<div class="media small-icon-media topmargin_5">
<div class="media-left">
<i class="fa fa-map-marker grey fontsize_18"></i>
</div>
<div class="media-body">
{{item.adress}}
</div>
</div>
<div class="media small-icon-media topmargin_5">
<div class="media-left">
<i class="fa fa-phone grey fontsize_18"></i>
</div>
<div class="media-body">
{{item.telephone}}
</div>
</div>
</div>
<div v-if="isNews" class="text-center">
<hr class="light-divider full-content-divider bottommargin_10">
<span class="date">
<i class="flaticon-clock-1 grey"></i>
<time class="entry-date">
{{item.date}}
</time>
</span>
</div>
</div>
</div>
</div>
</div>
`
});
var vm = new Vue({
el: '#cart',
name: 'cart',
delimiters: ['${', '}'],
data: {
complated: [],
continuing: [],
planned: [],
points: [],
infoSlider: [],
news: []
},
methods: {
openModal: function (item) {
Hub.$emit('open-modal', item);
},
closeModal: function () {
Hub.$emit('close-modal');
}
},
created() {
axios.get(url).then(res => {
var proje = res.data.projects[0];
this.complated = proje.complated;
this.continuing = proje.continuing;
this.planned = proje.planned;
this.points = res.data.paymentPoints;
this.infoSlider = res.data.sliderÄ°nfos;
this.news = res.data.news;
})
.catch(e => {
console.log(e);
})
}
});
When I click openModal(item) button give me error ;
Property or method "openModal" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I can not use the openModal function in component.
I can use the function in news.twig without any problems, but then I can not use the component. Can you help me?
You are using openModal in cart component, but that method is defined in root component.
According to Vue's documentation:
Everything in the parent template is compiled in parent scope;
everything in the child template is compiled in the child scope.
in my case need to define variable in vuejs
like this
<script>
export default {
name: "MegaMenu",
props: {
categories: Array,
},
}

Alpacajs form data not submitting if user does not tab off textbox

I am having an issue with alpacajs form not submitting data from a textbox if the user does not tab off the textbox before clicking the submit button, if the user has 5 fields to fill out all but the last one is included in the json results.
<div class="panel-group" id="accordian" role="tablist">
<div class="panel-placeholder"></div>
</div>
<div>
<button class="btn btn-default" type="submit">Submit</button>
</div>
<script id="panel-template" type="text/x-handlebars-template">
{{#each panels}}
<div class="panel panel-primary">
<div class="panel-heading">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#{{panelId}}">
{{title}}
</a>
</h4>
</div>
<div id="{{panelId}}" class="panel-collapse collapse in">
<div class="panel-body">
{{#each sections}}
<div id={{formId}}></div>
{{/each}}
</div>
</div>
</div>
{{/each}}
</script>
and the js code
function onData(formData) {
var friendlyData = "";
// Grab the template script
var templateScript = $("#panel-template").html();
// Compile the template
var compiledTemplate = Handlebars.compile(templateScript);
var compiledHtml = compiledTemplate(formData);
// Add the compiled html to the page
$('.panel-placeholder').html(compiledHtml);
//submit all data
$("[type='submit']").click(function () {
var postData = {
data: JSON.stringify(formData),
friendlyData: friendlyData,
storeData: storeData,
origUrl: origUrl
}
$.ajax({
url: apiUrl + "submit/",
data: postData,
dataType: 'json',
type: 'POST',
success: function (data) {
alert("Data was saved");
},
error: function (err) {
alert(err.statusText);
}
});
});
}
Any help with this would be great, I have not been able to find a solution.
Thank you

Infinity Ajax Request When Drop Change in Knockout MVC

I am using Knockout MVC in my project. I try to pass the viewModel to when Drop Down changing . but when I try this method call several times and the alert "ok" invoke continuesley. Can any one please help me on this??
$(function () {
$('#rmch').change(function () {
$.ajax({
url: '#Url.Action("DropChange", "Home")',
type: 'POST',
data: ko.mapping.toJSON(viewModel),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (data) {
if (data.redirect) {
location.href = resolveUrl(data.url);
}
else {
//ko.applyBindings(viewModel, document.getElementById("p_scentsFH"));
alert("Ok");
ko.mapping.fromJS(data, viewModel);
}
},
error: function (error) {
alert("There was an error posting the data to the server: " + error.responseText);
},
});
});
});
My Json Method
public JsonResult DropChange(HotelModel hotelmod)
{
//hmodel.RoomModel = new List<RoomModel>();
//for (int i = 1; i <= hmodel.NoOfRooms; i++)
//{
// hmodel.RoomModel.Add(new RoomModel { adultsDrp = ListItems.GetList(1, 6), childDrop = ListItems.GetList(0, 5) });
// //hmodel.RoomModel.Add(new RoomModel { });
//}
var jjj = JsonConvert.SerializeObject(hotelmod);
return Json(hotelmod);
}
My View
<div class="search-tab-content">
<div class="tab-pane fade active in" id="hotels-tab">
<form id="searchfrm">
<div class="title-container">
<h2 class="search-title">Search and Book Hotels</h2>
<p>We're bringing you a new level of comfort.</p>
<i class="soap-icon-hotel"></i>
</div>
<div class="search-content">
<h5 class="title">Where</h5>
<label>Your Destination</label>
#ko.Html.TextBox(m => m.Destination, new { #class = "input-text full-width", #placeholder = "Any destination, country, city code" })
#ko.Html.Hidden(new { #Id = "DesCode" }).Value(m => m.DesCode)
<hr>
<h5 class="title">When</h5>
<div class="row">
<div class="col-xs-4">
<label>Check In</label>
<div class="datepicker-wrap">
#ko.Html.TextBox(m => m.CheckInDate, new { #class = "input-text full-width" })
</div>
</div>
<div class="col-xs-4">
<label>Check Out</label>
<div class="datepicker-wrap">
#ko.Html.TextBox(m => m.CheckOutDate, new { #class = "input-text full-width" })
</div>
</div>
<div class="col-xs-4">
<label>ROOMS</label>
<div class="selector">
#ko.Html.DropDownList(m => m.RoomList, new { #class = "full-width jkl", #id = "rmch" }, "Text", "Value").Value(m => m.NoOfRooms)
</div>
</div>
</div>
<hr>
<div id="p_scentsFH">
#using (var rmModel = ko.Foreach(m => m.RoomModel))
{
<h5 class="title">Room 1</h5><div class="row">
<div class="col-xs-3">
<label>ADULTS</label>
<div class="selectorgen">
#rmModel.Html.DropDownList(m => m.adultsDrp, new { #class = "full-width" },"Text","Value").Value(m=>m.adultscount)
</div>
</div>
<div class="col-xs-3">
<label>KIDS</label>
<div class="selectorgen">
#rmModel.Html.DropDownList(m => m.childDrop, new { #class = "full-width" }, "Text", "Value").Value(m => m.childcount)
</div>
</div>
<div class="agecls">
#using(var chage=rmModel.Foreach(m=>m.childage))
{
<div class="col-xs-3">
<label>Child</label>
<div class="selectorgen">
#chage.Html.DropDownList(m => m.ageDrop, new { #class = "full-width" },"Text","Value").Value(m=>m.Age)
</div>
</div>
}
</div>
</div><hr>
}
</div>
<button type="submit" class="full-width uppercase">Search Cheap Hotels</button>
</div>
}
</form>
</div>
</div>
I believe that when the redirection did not happen then on else part your view model binding is causing your dropdown value to get changed and hence the dropdown on change event triggers again and again. Make sure you are not changing the selected item of your dropdown from ajax call.

Undefined property knockout.js + mvc

I have a masterDetails view that I populate with some data from a db (it populates fine). I added a button to the master details view, to add a step to my workflow.
My Viewmodel:
/// <reference path="_references.js" />
var viewModel = function (data) {
var self = this;
self.SelectedWorkflow = ko.observable({
Steps: ko.observableArray([]),
Name: ko.observable("")
});
self.Workflows = ko.observableArray(data);
self.addStep = function() {
self.Steps.push(new Step(SelectedWorkflow, "Assignment here", "01/01/2014", "dd:mm:ss", "mail"));
};
};
function Step(workflow,assignment, enddate, reminder, mailaddresses, type) {
var self = this;
self.Workflow = workflow;
self.StepNumber = 0;
self.Assignment = assignment;
self.Enddate = enddate;
self.Reminder = reminder;
self.MailAddresses = mailaddresses;
self.Type = type;
};
/// <reference path="workflowdetails-vm.js" />
$(document).ready(function () {
$.ajax({
url: "/WorkflowDetails/Index/",
type: "POST",
data: {},
success: function (data) {
var workflowlist = ko.mapping.fromJS(data.Workflows);
vm = new viewModel(workflowlist);
ko.applyBindings(vm);
}
});
$(".right-aligned-section").hide();
});
$(document).delegate(".show-details", "click", function () {
$(".right-aligned-section").fadeIn();
var workflow = ko.dataFor(this);
vm.SelectedWorkflow(workflow);
});
My View:
<div class="left-aligned-section">
<ul data-bind="foreach: Workflows()">
<li>
<div class="workflow-item-border">
<div>
<label data-bind="text: Name"></label>
</div>
<div>
<label data-bind="text: StartDate"></label>
</div>
<div>
Show Details
</div>
</div>
</li>
</ul>
</div>
<div class="right-aligned-section" data-bind="with: SelectedWorkflow">
<div class="steps-header">
<div class="left-aligned-div"><strong>Steps for </strong></div>
<div class="left-aligned-div" data-bind="text: Name"></div>
</div>
<button data-bind="click: addStep">add step</button>
<ul data-bind="foreach: Steps">
<li>
<div class="step-item-border">
<div>
<div class="step-label">Stepnumber: </div>
<div style="font-weight: bold" data-bind="text: StepNumber"></div>
</div>
<div>
<div class="step-label">Assignment: </div>
<div style="font-weight: bold" data-bind="text: Assignment"></div>
</div>
<div>
<div class="step-label">Mails: </div>
<div style="font-weight: bold" data-bind="text: MailAddresses"></div>
</div>
<div>
<div class="step-label">End Date: </div>
<div style="font-weight: bold" data-bind="text: Enddate"></div>
</div>
<div>
<div class="step-label">Type: </div>
<div style="font-weight: bold" data-bind="text: Type"></div>
</div>
</div>
</li>
</ul>
</div>
When I press the button - nothing happens. The error I receive is:
ReferenceError: Steps is not defined
I know what it means, but I'm not proficient enough in web development to actually fix it. Please help.
Probably this would work:
var viewModel = {};
viewModel.SelectedWorkflow = {
Steps: ko.observableArray([]),
Name: ko.observable("")
};
viewModel.Workflows = ko.observableArray(data);
viewModel.addStep = function () {
viewModel.SelectedWorkflow.Steps.push(
new Step(SelectedWorkflow, "Assignment here", "01/01/2014", "dd:mm:ss", "mail"));
}
}
You have forgotten about SelectedWorkflow.Steps I think...
Looks like you forgot a this before the Steps.push....
Edit: I have made some further changes to the click handler function
var viewModel = function (data) {
this.SelectedWorkflow = ko.observable({
Steps: ko.observableArray([]),
Name: ko.observable("")
});
this.Workflows = ko.observableArray(data);
this.addStep = function (selectedWorkflow) { // the current context is passed in from the click data-bind
selectedWorkflow.Steps.push(new Step(selectedWorkflow, "Assignment here", "01/01/2014", "dd:mm:ss", "mail"));
}
}
Edit: Fixed code

Categories

Resources