Symfony custom fields on add - forms

On my Symfony2 project I got a list of arguments :
<ul class="arguments" data-prototype="{{ form_widget(form.arguments.vars.prototype)|e('html_attr') }}">
{% for argument in form.arguments %}
{#<li>{{ form_row(argument) }}</li>#}
<li>
{{ form_row(argument.name, {'label': 'Name'}) }}
{{ form_row(argument.french_description) }}
{{ form_row(argument.english_description) }}
{{ form_row(argument.return) }}
{{ form_row(argument.type) }}
</li>
{% endfor %}
</ul>
But I got a button "Add arguments", creating a new < li >< /li > with arguments fields. The problem is the new argument don't got the custom label "Name".
EDIT :
I add the arguments with a buton calling a JS function to add my fields on the page. Then when I click on submit it save it to database.
$('#add_argument_link').on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// setup an "add a argument" link
var $newLinkLi = $('<li></li>');
// add a new argument form (see next code block)
addArgumentForm($collectionHolder, $newLinkLi);
addTagFormDeleteLink($newLinkLi);
});
Thanks for your help.

You can try something like that :
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var $container = $('div#name_of_your_div');
var index = $container.find(':input').length;
$('#add_argument').click(function(e) {
addArgument($container);
e.preventDefault();
return false;
});
if (index == 0) {
addArgument($container);
} else {
$container.children('div').each(function() {
addDeleteLink($(this));
});
}
function addArgument($container) {
var template = $container.attr('data-prototype')
.replace(/__name__label__/g, 'Argument number' + (index+1))
.replace(/__name__/g, index);
var $prototype = $(template);
addDeleteLink($prototype);
$container.append($prototype);
index++;
}
function addDeleteLink($prototype) {
var $deleteLink = $('Delete');
$prototype.append($deleteLink);
$deleteLink.click(function(e) {
$prototype.remove();
e.preventDefault();
return false;
});
}
});
</script>

Related

Adding a wp: featured image to loop when using a headless CMS via the Wordpress API and Vue.js

Would like to know how to implement the Wordpress Featured Image to multiple vue.js/nuxt.js based headless-CMS approaches using the Wordpress restful API.
Initially, I followed this immensely helpful tutorial headless-cms with nuxt and created a headless CMS via the wordpress api, and of course applied it to my use-case (here is a link to a live version nuxt-headless-cms-webapp. Unfortunately, I have been unable to figure out how to include a post's featured image as it is not covered in this particular tutorial. I then did a bit of research and ended up piecing together another project (vue.js), in which I was able to implement featured images. That being said I would like guidance with regard to implementing my working code in terms of the wp-featured image, to the original tutorial's project (as nuxt provides better routing and SEO options from my understanding. Thank you in advanced for any help!
First, here is the axios http request syntax found in the original tutorial (nuxt) project's index.js:
const siteURL = "https://indvillage.com"
export const state = () => ({
posts: [],
tags: []
})
export const mutations = {
updatePosts: (state, posts) => {
state.posts = posts
},
updateTags: (state, tags) => {
state.tags = tags
}
}
export const actions = {
async getPosts({ state, commit, dispatch }) {
if (state.posts.length) return
try {
let posts = await fetch(
`${siteURL}/wp-json/wp/v2/posts?_embed`
).then(res => res.json())
posts = posts
.filter(el => el.status === "publish")
.map(({ id, slug, title, excerpt, date, tags, content }) => ({
id,
slug,
title,
excerpt,
date,
tags,
content
}))
commit("updatePosts", posts)
} catch (err) {
console.log(err)
}
},
async getMedia({ state, commit }) {
if (state.media.length) return
try {
let media= await fetch(
`${siteURL}/wp-json/wp/v2/media?_embed`
).then(res => res.json())
commit("updatePosts", media)
} catch (err) {
console.log(err)
}
},
async getTags({ state, commit }) {
if (state.tags.length) return
let allTags = state.posts.reduce((acc, item) => {
return acc.concat(item.tags)
}, [])
allTags = allTags.join()
try {
let tags = await fetch(
`${siteURL}/wp-json/wp/v2/tags?page=1&per_page=40&include=${allTags}`
).then(res => res.json())
tags = tags.map(({ id, name }) => ({
id,
name
}))
commit("updateTags", tags)
} catch (err) {
console.log(err)
}
}
}
Next, we have the index.vue page where the above logic is implemented.
template>
<div>
<app-masthead></app-masthead>
<div class="posts">
<main>
<div class="post" v-for="post in sortedPosts" :key="post.id">
<h3>
<a :href="`blog/${post.slug}`">{{ post.title.rendered }}</a>
</h3>
<small>{{ post.date | dateformat }}</small>
<div v-html="post.excerpt.rendered"></div>
<a :href="`blog/${post.slug}`" class="readmore slide">Read more ⟶</a>
</div>
</main>
<!--<aside>
<h2 class="tags-title">Tags</h2>
<div class="tags-list">
<ul>
<li
#click="updateTag(tag)"
v-for="tag in tags"
:key="tag.id"
:class="[tag.id === selectedTag ? activeClass : '']"
>
<a>{{ tag.name }}</a>
<span v-if="tag.id === selectedTag">✕</span>
</li>
</ul>
</div>
</aside>-->
</div>
</div>
</template>
<script>
import AppMasthead from "#/components/AppMasthead.vue";
export default {
components: {
AppMasthead
},
data() {
return {
selectedTag: null,
activeClass: "active"
};
},
computed: {
posts() {
return this.$store.state.posts;
_embed = true;
},
tags() {
return this.$store.state.tags;
},
sortedPosts() {
if (!this.selectedTag) return this.posts;
return this.posts.filter(el => el.tags.includes(this.selectedTag));
}
},
created() {
this.$store.dispatch("getPosts");
},
methods: {
updateTag(tag) {
if (!this.selectedTag) {
this.selectedTag = tag.id;
} else {
this.selectedTag = null;
}
}
}
};
Here is a link to my project with working wordpress featured images! https://indvillage.netlify.app/
And here is the logic associated with the axious http request I used.
The question is how, do I include my logic in terms of the wp featured image to the initial nuxt tutorial without breaking things:
export default {
data() {
return {
postsUrl: "https://indvillage.com/wp-json/wp/v2/posts",
queryOptions: {
per_page: 6,
page: 1,
_embed: true
},`
posts: []
};
},
methods: {
// Get recent posts from wp
getRecentMessages() {
axios
.get(this.postsUrl, { params: this.queryOptions })
.then(response => {
this.posts = response.data;
console.log("Posts retrieved!");
})
//document.getElementById("test").id = "testing";
.catch(error => {
console.log(error);
});
},
getPostDate(date) {
return moment(date).format("111");
},
},
mounted() {
this.getRecentMessages();
}
}
Next, here is the App.vue template that displays the parsed information:
<template id="app">
<body>
<div class="row container">
<!-- looping through and displaying the array bound posts in HTML -->
<div class="col s4 m4" v-for="(post, index) in posts" :key="index" :id="'post'+index">
<div class="card" id="test">
<div class="card-image">
<!-- rendering the post's wp:featuredmedia in the image portion of the html/css card -->
<img
v-if="post._embedded['wp:featuredmedia']"
:src="post._embedded['wp:featuredmedia'][0].source_url"
/>
</div>
<!-- rendering the post.excerpt to the html/css card container -->
<div class="card-content" v-html="post.excerpt.rendered"></div>
<div class="card-action">
<!-- rendering the post title to the action portion of the html/css card -->
{{ post.title.rendered }}
</div>
</div>
</div>
</body>
</template>
Please let me know if anyone has any suggestions with regard to implementing wp:featuredmedia to the code derived from the first project/tutorial (nuxt.js)
Thanks again! Feel free to email with further questions

Concatenating multiple classes in Vue.js

I want to write in less code a function that will add the active classname and automatically removes all the other active class names. But there is also a unique class name needed for JavaScript in my case. But want to put that all in class name. How can I make this a valid classname. Is there a way to do that so it will not conflict with each other.
<ul class="three">
<li
v-for="(post, index) in listData.data"
:key="index"
:class="'list-item unordered-list ' + post.name.toLowerCase() + { active : activeName == post.name}"
#click="showInfo(post.name, post.description)">
{{ post.name }}
</li>
</ul>
Have a look at the object syntax or array syntax of class binding, which allows binding to an object or array returned by a value. That way you can simplify complex class or style combinations by calling a function from the template, like the example from the docs:
<div v-bind:class="classObject"></div>
...
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
<ul class="three">
<li
v-for="(post, index) in listData.data"
:key="index"
:class="['list-item', 'unordered-list ', post.name.toLowerCase(), { active: activeName == post.name }]"
#click="showInfo(post.name, post.description)">
{{ post.name }}
</li>
</ul>

How to create Date(now) within Vue.js to use in a computed property?

I have an array of calendar events with a pre-formatted date: 20190517T010000Z.
I need to filter the array based upon the following:
1) Future Events (any events occurring greater than NOW)
2) Past Events (any events that occurred less than NOW)
3) New Events created within the past 7 days (NOW - 7 days)
I have an example hardcoded below - but need NOW to be dynamic and based upon the user's system time. I can't figure out how to get NOW in the same format as the datetime format I'm getting in the my array. Also, I don't know where in my code it should reside (within a separate JS file or within the Vue component)?
Once I have it working within the component listed below, it will be moved to a VUEX Getter.
<template>
<div class="text-left m-4 p-4">
<div>
Total Number Events: {{ allEvents.length }}
</div>
<div>
Events Created In Last 7 Days: {{ createdEvents }}
</div>
<div>
Future Events: {{ futureEvents }}
</div>
<div>
Past Events: {{ pastEvents }}
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['allEvents']),
futureEvents () {
return this.$store.state.allEvents.filter(allEvents => allEvents.dtstart >= '20190517T010000Z').length
},
pastEvents () {
return this.$store.state.allEvents.filter(allEvents => allEvents.dtstart <= '20190517T235900Z').length
},
createdEvents () {
return this.$store.state.allEvents.filter(
allEvents => allEvents.created >= '20190511T235900Z' && allEvents.created <= '20190517T235900Z' )
.length
}
},
}
</script>
The above code works - but it is hardcoded right now and it needs to be dynamic. Any thoughts or suggestions would be welcome.
All your date time objects should be persisted as UNIX timestamps (UTC seconds since 1/1/1970). This will allow you to compare times across systems no matter the timezone.
Provided that the events in your Vuex store are stored with dstart being a UNIX timestamp, the following snippet should give you the dynamic behaviour you want.
I've created a now attribute in the component's data. This gets updated every second, causing the computed properties to refresh.
Additionally, if the allEvents property in the Vuex store updates, the computed properties will also refresh.
<template>
<div class="text-left m-4 p-4">
<div>
Total Number Events: {{ allEvents.length }}
</div>
<div>
Events Created In Last 7 Days: {{ createdEvents }}
</div>
<div>
Future Events: {{ futureEvents }}
</div>
<div>
Past Events: {{ pastEvents }}
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data () {
return {
now: new Date().getUTCSeconds()
}
},
created () {
this.scheduleUpdateNow();
},
methods: {
updateNow() {
this.now = new Date().getUTCSeconds();
this.scheduleUpdateNow();
},
scheduleUpdateNow() {
setTimeout(this.updateNow, 1000);
}
},
computed: {
...mapState(['allEvents']),
futureEvents () {
return this.$store.state.allEvents.filter(allEvents => allEvents.dtstart > this.now).length
},
pastEvents () {
return this.$store.state.allEvents.filter(allEvents => allEvents.dtstart <= this.now).length
},
createdEvents () {
return this.$store.state.allEvents.filter(
allEvents => allEvents.created >= this.now && allEvents.created <= this.now).length
}
}
}
</script>

Using Protractor: Switch to iframe using browser.switchTo().frame

So I have already written the testing script which:
1) Logs into the application framework, then
2) Clicks menu to launch the app which I am testing ("MyAwesomeApp.html" for this post)
And my main problem is: In navpanel-spec.js below, I want to target the https://server/apps/Default.aspx?r=1 URL, then click within the iframe where MyAwesomeApp is running.
**** ERROR Trying to switch to the iframe this way, but it does NOT work:
browser.switchTo().frame(element(by.id('1')).getWebElement());
Error in cmd prompt:
Started
[15:43:29] E/launcher - No element found using locator: By(css selector, *[id="\31 "])
...
sat-navpanel-spec.js:52:24)
So there are two URLs going on here:
1) https://server/apps/Default.aspx?r=1 (the main app framework with menu system in top nav).
2) https://server/apps/MyAwesomeApp.html (the web app which the test script launches within the <iframe> tag.
The html looks like this, where the application renders within the <iframe> :
<body>
<div id="top">
<!-- top nav menu systems rendered here -->
</div>
<div id="middle">
<div id="m1">
<div id="m2" class="hidden">
<div id="m3">
<div id="right" class="hidden">
<div>
<div id="frame_holder" style="height: 940px;">
<iframe style="width: 100%; height: 100%;" name="1" id="1" src="https://server/apps/MyAwesomeApp.html">
</iframe>
</div>
</div>
</div>
</div>
<div id="left" style="display: none;"></div>
</div>
</div>
</div>
</body>
In my Protractor.config.js file I have a few specs :
specs: [
'spec/login.js',
'spec/launch-awesome-app.js',
'spec/navpanel-spec.js',
'spec/another-spec.js',
'spec/yet-another-spec.js'
]
login.js and launch-awesome-app.js work fine. They log into the menu system, then click thru the menus in order to launch myAwesomeapp - no problem.
MY PROBLEM:
In navpanel-spec.js I want to target the https://server/apps/Default.aspx?r=1 URL, then click within the iframe where MyAwesomeApp is running.
However, it is NOT selecting any of my elements.
If I target https://server/apps/MyAwesomeApp.html in navpanel-spec.js, of course it launches a new browser window and runs the test just fine.
Here's my navpanel-spec.js test spec:
describe('Testing My Awesome App', function () {
var panelObj = new PanelObjects();
var urlDefault = 'https://server/apps/Default.aspx?r=1';
var urlApp = 'https://server/apps/MyAwesomeApp.html';
browser.get(urlApp); // Runs my AwesomeApp tests okay, HOWEVER it launches a new browser window.
browser.get(urlDefault); // Launches app framework with top nav menus and embedded <iframe>,
// HOWEVER I cannot select iframe and successfully run tests here.
beforeEach(function () {
browser.sleep(5000);
browser.waitForAngular();
});
// USE-CASE OBJECT !!
var items = browser.params.useCaseJsonFile["navigatePanels"];
browser.getAllWindowHandles().then(function (handles) {
handles.map(function (win, idx) {
browser.driver.getCurrentUrl().then(function (curr) {
if (curr.indexOf('Default.aspx') >= 0) {
browser.driver.switchTo().window(handles[idx]);
}
});
});
});
browser.switchTo().frame(element(by.id('1')).getWebElement());
var testId = element(by.id('middle'));
console.log(testId);
items.map(function (item) {
if (item.enableTest) {
var specItem = it(item.name, function () {
console.log('------------------------------');
console.log('---- ' + item.describe);
browser.waitForAngular();
// select panels, etc..
panelObj.panelClick(item.panelName).then(function () {
// ...
});
panelObj.getPanelText(item.panelName).then(function (title) {
expect(title).toContain(item.panelTitle);
});
});
}
});
});
UPDATE
var LoginObjects = require('../pageObjects/login-objects.js');
describe('Testing My Awesome App', function () {
var panelObj = new PanelObjects();
var loginObj = new LoginObjects();
//var urlDefault = 'https://server/apps/Default.aspx?r=1';
//browser.get(urlApp); // Runs my AwesomeApp tests okay, HOWEVER it launches a new browser window.
browser.ignoreSynchronization = true;
// LOGIN AND LAUNCH APP !!!
loginObj.Login();
loginObj.Launch();
beforeEach(function () {
browser.sleep(5000);
browser.waitForAngular();
});
// USE-CASE OBJECT !!
var items = browser.params.useCaseJsonFile["navigatePanels"];
// SWITCH TO iframe ELEMENT
loginObj.switchWindowAndFrame();
items.map(function (item) {
if (item.enableTest) {
var specItem = it(item.name, function () {
console.log('------------------------------');
console.log('---- ' + item.describe);
browser.waitForAngular();
// select panels, etc..
panelObj.panelClick(item.panelName).then(function () {
// ...
});
panelObj.getPanelText(item.panelName).then(function (title) {
expect(title).toContain(item.panelTitle);
});
});
}
});
});
and my page objects :
module.exports = function(){
this.Login = function(){
var url = browser.params.loginUrl;
browser.driver.get(url);
browser.sleep(200);
var userName = browser.params.credential.userId;
var password = browser.params.credential.password;
element(by.id('username')).clear().then(function(){
element(by.id('username')).sendKeys(userName);
element(by.id('password')).sendKeys(password);
});
browser.sleep(1000);
var that = this;
var submitElement = element(by.id('bthLogin'));
submitElement.click().then(function () {
browser.getAllWindowHandles().then(function (handles) {
// LOGIN MESSAGE WINDOW
browser.driver.getCurrentUrl().then(function(curr){
if (curr.indexOf('LoginMsg.aspx') >= 0){
// Do we really need to close the login successful browser ???
browser.driver.close();
}
});
browser.driver.switchTo().window(handles[1]);
});
});
},
this.Launch = function(){
var sel = '#TheMenu1 > ul > li:first-child';
var elem = element(by.css(sel));
elem.click().then(function(){
browser.sleep(1000);
var elem2 = element(by.cssContainingText('.rmLink', 'The First Menu Item'));
elem2.click();
// Select menu item; sleep before attempting to click().
var subElem = element(by.cssContainingText('.rmLink', 'My Awesome App'));
browser.sleep(1000);
subElem.click();
browser.waitForAngular();
});
},
this.switchWindowAndFrame = function(){
browser.getAllWindowHandles().then(function (handles) {
handles.map(function(win, idx){
browser.driver.getCurrentUrl().then(function(curr){
if (curr.indexOf('Default.aspx') >= 0){
browser.driver.switchTo().window(handles[idx]);
}
});
});
});
browser.switchTo().frame(element(by.css('[name="1"]')).getWebElement());
}
};
As mentioned in the comments above, protractor has a bug which prefixes '\3' to your id element with number.
The temporary way is to change you locator. :P

Mongodb return an Empty array

I am trying to make a range query and show the data in my ejs view
EJS View "historicos.ejs"
<h1>historicos</h1>
<form action="/historicos/buscar">
<input type="text" name='inicio' id='inicio'></li>
<input type="text" name='final' id='final'></li>
<br>
<button type="submit">Buscar</button>
</form>
<% if(typeof his !== 'undefined'){
his.forEach(function(dati){%>
<%= dati.temp %>
<%= dati.hum %>
<%= dati.date %>
<br/>
<%});
}; %>
I am using a controller with 2 methods, 1 for render the page and one to manage the query.
Controller "grafi.js"
var Si = require('../models/sis');
exports.getPagehis = function(req, res) {
if (req.user) return res.redirect('/');
res.render('historicos');
};
exports.getHis= function(req, res, next) {
ini = req.body.inicio;
fin = req.body.final;
console.log(ini)
console.log(fin)
Si.find({"date": {"$gte":ini, "$lt":fin}},function(err, his) {
console.log(his);
if(err) return next(err);
res.render('historicos', {
his:his
});
});
};
and this is my router file
router.get('/historicos', hiController.getPagehis);
router.get('/historicos/buscar', hiController.getHis);
if I write manually ini and fin to make the query(using this format YYYY-MM-DDThh:mm), I mean without the req.body.inicial and req.body.final, it work well but when I request the information from the view I got this fields undifined.
what can i do to solve this?
the problem was form and the request in general adding method="get" in the form and update the controller with:
var Url = require('url');
...
queryparams = Url.parse(req.url,true).query;
ini = queryparams.ini;
fi = queryparams.fi;
... make the query to mongo db
with this code is possible to get the values in the input files passed in the url