How to manipulate forms with Mootools - forms

I'm trying to manipulate forms with Mootools. My purpose is to inject the response content of a form into a div element named result.
Here a code that works, but it replaces the content of the result div. This is not what I want : I want to ADD the form response content to the result div existing content. I just can't find on the web how to do this, and I've tried many things that are not working ... Please help
window.addEvent('domready', function() {
$('myform').addEvent('submit', function(e) {
e.stop();
var result = $('result').empty();
this.set('send',{
url: this.get('action'),
data: this,
onSuccess: function() {
result.set("html", this.response.text);
}
}).send();
});
});

If it's only text you want to add, just remove the empty method, and replace result.set() with result.appendText().
If you need to append an element tree, repeat the first step, and do:
onSuccess: function(){
Elements.from(this.response.text).inject(result);
}
Btw. It's all in the documentation - http://mootools.net/docs/core/Element/Element

Related

How to use the nextHandler functionality as shown in the Infinite Ajax Scroll JSON example

I’m hoping to be able to use Infinite Ajax Scroll for a project I’m working on.
I’ve been looking at the Infinite Scroll JSON example here (https://infiniteajaxscroll.com/examples/json/) and I’m finding it difficult to understand how it works. I was wondering if there is any further documentation or code examples on how to use a JS or jQuery handler as shown in the example.
Ultimately what I want to do is load my container "items" using my own ajax function and then have Infinite Ajax Scroll display them. I want to do this because my container "items" are not located at URLs, but are saved as Wordpress transients.
Any help I could get with this would be very much appreciated.
Thanks,
David.
Thank you for your question. The docs on using the nextHandler could indeed use improvement. Regardless, I'll try to explain how it works.
Normally IAS uses a selector to find the url of the next page. Then it loads the page and extracts the elements and appends them to the DOM. If you use the nextHandler, you will completely bypass this behavior. That means you will have to fetch data (in this case JSON) yourself and also insert new elements in the DOM.
Here is an example with comments to explain what it does.
First, let's assume our movie(1..n).json has the following format:
[
{
Title: 'item1',
Plot: 'description1'
}, {
Title: 'item2',
Plot: 'description2'
}
]
Now the implementation of the nextHandler:
import InfiniteAjaxScroll from "#webcreate/infinite-ajax-scroll";
function nextHandler(pageIndex) {
// we use the fetch api to load the next page. Any http client library will do, like axios.
return fetch("./static/movies"+pageIndex+".json")
// use the response as json
.then((response) => response.json())
// process the actual json data
.then((jsonData) => {
// create an array to store our html elements in memory
let elements = [];
// we loop over the items in our json and create an html element for each item
jsonData.forEach(function (item) {
const template = `<div class="item">
<h1>${item.Title}</h1>
<p>${item.Plot}</p>
</div>`;
const element = document.createElement("div");
element.innerHTML = template.trim();
elements.push(element.firstChild);
});
// now use IAS's append method to insert the elements
// it's import that we return the append result, as it's an promise
return this.append(elements);
})
// page 3 returns a 404, returning false here indicates there are no more pages to load
.catch(() => false);
}
window.ias = new InfiniteAjaxScroll(".container", {
item: ".item",
next: nextHandler,
pagination: false
});
I also prepared an interactive demo on Codesandbox:
https://codesandbox.io/s/serene-einstein-f73em

How to seperate Vue logic in a laravel app based on layout and page templates

I have a laravel app and a Vue instance attached to the body (or a div, just inside the body).
const app = new Vue({
el: '#app'
});
I think it makes sense to use the Vue instance for stuff relating to the layout (eg header, nav, footer logic).
Now I have a form that is visible on a specific route (e.g. example.com/thing/create). I want to add some logic to it, e.g. hiding a field based on selected option in the form. It is logic meant for just this form (not to be reused). I prefer not to put all the logic inline with the form but put it in the app.js. I could put it in the Vue instance bound to the body but that sounds odd as it only applies to the form that is much deeper into the dom.
I want to leave the markup of the form in the blade template (that inherits the layout).
I tried creating a component but am not sure how to bind this inside the main Vue instance. What is the best way to handle things for this form, put it in the app.js and have it somewhat structured, putting the variables somewhat into scope. Or is it really necessary to remove the main Vue instance bound to the full layout code?
What I tried was something like this, but it does not work (attaching it to the <form id="object-form"> seems to fail:
var ObjectForm = {
template: function() { return '#object-form'},
data: function() {
return {
selectedOption: 1
}
},
computed: {
displayField: function() {
// return true or false depending on form state
return true;
}
}
};
Things do work if I remove the #app Vue instance or when I put everything directly in the app Vue instance. But that seems messy, if I have similar variables for another form they should be seperated somewhat.
I would appreciate some advice regarding the structure (differentiate page layout and page specific forms) and if possible some example to put the form logic inside the main app.js.
I hope this helps kind of break things down for you and helps you understand Vue templating.
It is best to take advantage of Vue's components. For you it would look something like this. Some of this code depends on your file structure, but you should be able to understand it.
In your app.js file (or your main js file)
Vue.component('myform',require('./components/MyForm.vue'));
const app = new Vue({
el: "#app"
});
Then create the MyForm.vue file
<template>
<form>
Put Your Form Markup Here
</form>
</template>
<script>
// Here is where you would handle the form scripts.
// Use methods, props, data to help set up your component
module.exports = {
data: function() {
return {
selectedOption: 1
}
},
computed: {
displayField: function() {
// return true or false depending on form state
return true;
}
},
methods: {
// add methods for dynamically changing form values
}
}
</script>
Then you will be able to just call in your blade file.
<myform></myform>
I found out how to do it. The trick was to use an inline template. Surround the form in the view with:
<object-form inline-template>
<form>...</form>
</object-form>
Where object-form is the name of the component. In the ObjectForm code above I remove the template, like this:
var ObjectForm = {
data: function() {
return {
selectedOption: 1
}
},
computed: {
displayField: function() {
// return true or false depending on form state
return true;
}
}
};
I attach the component within the root vue app like this:
const app = new Vue({
el: 'body',
components: {
'object-form': ObjectForm
}
});
This way I can use the form as it was generated from the controller and view and I can separate it from the root (attached to body) methods and properties.
To organize it even better I can probably store the ObjectForm in a seperate .vue file the way #Tayler Foster suggested.

Protractor element handling

I have a question regarding how protractor handles the locating of elements.
I am using page-objects just like I did in Webdriver.
The big difference with Webdriver is that locating the element only happens when a function is called on that element.
When using page-objects, it is advised to instantiate your objects before your tests. But then I was wondering, if you instantiate your object and the page changes, what happens to the state of the elements?
I shall demonstrate with an example
it('Change service', function() {
servicePage.clickChangeService();
serviceForm.selectService(1);
serviceForm.save();
expect(servicePage.getService()).toMatch('\bNo service\b');
});
When debugging servicePage.getService() returns undefined.
Is this because serviceForm is another page and the state of servicePage has been changed?
This is my pageobject:
var servicePage = function() {
this.changeServiceLink = element(by.id('serviceLink'));
this.service = element(by.id('service'));
this.clickChangeService = function() {
this.changeServiceLink.click();
};
this.getService = function() {
return this.service.getAttribute('value');
};
};
module.exports = servicePage;
Thank you in advance.
Regards
Essentially, element() is an 'elementFinder' which doesn't do any work unless you call some action like getAttribute().
So you can think of element(by.id('service')) as a placeholder.
When you want to actually find the element and do some action, then you combine it like element(by.id('service')).getAttribute('value'), but this in itself isn't the value that you are looking for, it's a promise to get the value. You can read all about how to deal with promises elsewhere.
The other thing that protractor does specifically is to patch in a waitForAngular() when it applies an action so that it will wait for any outstanding http calls and timeouts before actually going out to find the element and apply the action. So when you call .getAttribute() it really looks like
return browser.waitForAngular().then(function() {
return element(by.id('service')).getAttribute('value');
});
So, in your example, if your angular pages aren't set up correctly or depending on the controls you are using, you might be trying to get the value before the page has settled with the new value in the element.
To debug your example you should be doing something like
it('Change service', function() {
servicePage.getService().then(function(originalService) {
console.log('originalService: ' + originalService);
});
servicePage.clickChangeService();
serviceForm.selectService(1);
serviceForm.save();
servicePage.getService().then(function(newService) {
console.log('newService: ' + newService);
});
expect(servicePage.getService()).toMatch('\bNo service\b');
});
The other thing that I'm seeing is that your pageObject appears to be a constructor when you could just use an object instead:
// name this file servicePage.js, and use as 'var servicePage = require('./servicePage.js');'
module.exports = {
changeServiceLink: element(by.id('serviceLink')),
service: element(by.id('service')),
clickChangeService: function() {
this.changeServiceLink.click();
},
getService: function() {
return this.service.getAttribute('value');
}
};
Otherwise you would have to do something like module.exports = new servicePage(); or instantiate it in your test file.
When you navigate another page, the web elements will be clear, that you selected. So you have to select again. You can select all elements that is in a page of HTML. You can click that you see. So the protactor + Selenium can decide what is displayed.
You have a mistake in your code, try this:
expect(servicePage.getService()).toMatch('\bNo service\b');

Ignore placeholder values on ajax form submit

I'm using the jQuery ajax form plugin in my WordPress plugin's settings page. Before I started using ajax, I had this script that compared text input values to placeholder values, and if they matched, set the text input value to null. But it no longer works now that I'm using ajax. With the jQuery ajax form plugin, I can pass arguments in a beforeSerialize function, or in a beforeSubmit function. I think it would need to be done in the beforeSerialize. Anyway, I'm not sure how to make this work. Here is the script that was working before I switched to ajax:
$('[placeholder]').focus(function() {
var input = $(this);
if (input.val() == input.attr('placeholder')) {
input.val('');
input.removeClass('placeholder');
}
}).blur(function() {
var input = $(this);
if (input.val() == '' || input.val() == input.attr('placeholder')) {
input.addClass('ssfa-placeholder');
input.val(input.attr('placeholder'));
}
}).blur().parents('form').submit(function() {
$(this).find('[placeholder]').each(function() {
var input = $(this);
if (input.val() == input.attr('placeholder')) {
input.val('');
}
})
});
And here is my current script for the ajax form submit:
var svn = $("#ssfa-saving"),
bck = $("#ssfa-saving-backdrop"),
svd = $("#ssfa-settings-saved");
$("#ssfa-form").ajaxForm({
beforeSend: function() {
svn.fadeIn('slow');
bck.fadeIn('fast');
},
success: function(){
svn.fadeOut('slow');
svd.delay(1000).fadeIn('slow').delay( 2500 ).fadeOut('slow');
bck.delay( 4500 ).fadeOut('slow');
}
});
Any ideas on how I can get the ajax submit (either beforeSerialize or beforeSend ) to ignore placeholder values? This first script above was a really simple solution for regular post submit. I'm hoping I can find something just as simple for ajax.
UPDATE
I worked out a basic way of doing it but it involves calling each text field that has a placeholder, so it's not exactly elegant like the original script, but this is functional:
$("#ssfa-form").ajaxForm({
beforeSerialize: function() {
var permex = $('input#exclusions');
$('input[id^=bs]').each(function(){
var bs = $(this);
if (bs.val() === 'Display Name')
bs.removeAttr('value');
});
$('input[id^=custom_]').each(function(){
var cs = $(this);
if (cs.val() === 'classname1|Display Name 1, classname2|Display Name 2')
cs.removeAttr('value');
});
if (permex.val() === '.avi, My Embarrasing Photograph, .tif')
permex.removeAttr('value');
},
beforeSend: function() {
etc.
And since it's a placeholder text, the text doesn't actually disappear when the value attribute is removed, so no one is really the wiser. I'm not over the moon with this, but it works. If I had a much larger form, this wouldn't be workable.
Open to better ideas....
Well, I played around with it quite a bit more and found a way to get the original code to work with ajax submit. It's quite simple actually. I just had to specify the element within which to search for the placeholder attr. Here it is:
beforeSerialize: function() {
$("#ssfa-form").find('[placeholder]').each(function() {
var input = $(this);
if (input.val() == input.attr('placeholder')) {
input.val('');
}
})
},
etc.
To track the issue, see:
https://github.com/mathiasbynens/jquery-placeholder/issues/30
https://github.com/mathiasbynens/jquery-placeholder/issues/197

Jquery UI Autocomplete

I am trying to get the Jquery UI autocomplete working on AJAX loaded dynamic fields in div #right
I do not fully understand the code below.
$("#right").delegate(".drugName", "focus", function(){
//attach autocomplete
$(".drugName").autocomplete({
//define callback to format results
source: function(req, add){
//pass request to server
$.getJSON("druglist.php?callback=?", req, function(data) {
//create array for response objects
var suggestions = [];
//process response
$.each(data, function(i, val){
suggestions.push(val.name);
});
//pass array to callback
add(suggestions);
});
},
});
});
But it works in Chrome/FF. However it seems to be killing AJAX loading in Internet Explorer causing the application to be non - functional
The error returned is
SCRIPT1028: Expected identifier, string or number ajaxfunctions.js, line 41 character 6
The error in the console refers to the brackets on the second last row.
I tried to work this out using the documentation, but couldnt get it to work :-(
Whats happening with the code & IE?
Pls help.
//pass array to callback
add(suggestions);
});
}, //OK the comma here was the problem
});
Got it working. this helped