JsViews: in-line template syntax for direct linked form element - templates

I saw an example of linking directly to a form element using JsViews, which I found to be preferable to encapsulating the whole form in a template. Here is a jsfiddle example of what I'm trying to do, which partially works:
http://jsfiddle.net/30jpdnkt/
var app = {
selections: {
things: [
{ Name: "thingName1", Value: "thingValue1" },
{ Name: "thingName2", Value: "thingValue2" },
{ Name: "thingName3", Value: "thingValue3" }
]
},
formData: {
selectedThing: "thingValue1",
}
};
//how do I reference this template in-line, outside of another wrapping template?
$.templates({
theTmpl: "#theTmpl"
});
$("#content").link(true, app);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://www.jsviews.com/download/jsviews.js"></script>
<script id="theTmpl" type="text/x-jsrender">
<select id="thingChoice" data-link="formData.selectedThing">
<option value="-">Please select</option>
{^{for selections.things}}
<option data-link="value{:Value} {:Name} selected{:~selected === Value}"></option>
{{/for}}
</select>
</script>
<div id="content">
<!--this part works-->
<input data-link="formData.selectedThing trigger=true"/>
<!--this part does not display-->
<span {{for data tmpl="theTmpl"/}}></span>
</div>
The data-linked INPUT tag is correctly bound to the object, but I cannot find a working example of how to reference a compiled template in-line without encapsulating the entire form in another template. That it's possible to use data-link syntax outside of a template gives hope that it may be possible with correct syntax.
Is this possible?

Yes it is possible - it is what I call top-level data-linking. There will be new documentation topics on this coming very soon, but meantime you have this sample:
http://www.jsviews.com/#samples/editable/toplevel-for
And your jsfiddle - which I updated to make it work fully: http://jsfiddle.net/30jpdnkt/1/
<div id="content">
<input data-link="formData.selectedThing trigger=true"/>
<span data-link='{for tmpl="theTmpl"}'></span>
</div>

Related

Nuxt JS Apollo data only available after page refresh

I am fetching some data using Apollo inside of Nuxt. Somehow, when navigating to that page I get an error of
Cannot read property 'image' of undefined
When I refresh the page, everything works as expected.
I have a found a few threads of people having similar issues but no solution seems to work for me :/
This is my template file right now:
/products/_slug.vue
<template>
<section class="container">
<div class="top">
<img :src="product.image.url"/>
<h1>{{ product.name }}</h1>
</div>
</section>
</template>
<script>
import gql from 'graphql-tag'
export default {
apollo: {
product: {
query: gql`
query Product($slug: String!) {
product(filter: { slug: { eq: $slug } }) {
slug
name
image {
url
}
}
}
`,
prefetch({ route }) {
return {
slug: route.params.slug
}
},
variables() {
return {
slug: this.$route.params.slug
}
}
}
}
}
</script>
Basically the $apolloData stays empty unless I refresh the page. Any ideas would be much appreciated
EDIT
Got one step closer (I think). Before, everything (image.url and name) would be undefined when navigating to the page for the first time.
I added:
data() {
return {
product: []
};
}
at the top of my export and now at least the name is always defined so if I remove the image, everything works as expected. Just the image.url keeps being undefined.
One thing I noticed (not sure how relevant) is that this issue only occurs using the , if I use a normal a tag it works but of course takes away the vue magic.
EDIT-2
So somehow if I downgrade Nuxt to version 1.0.0 everything works fine
I stumbled on this issue as well, and found it hidden in the Vue Apollo documents.
Although quite similar to the OP's reply, it appears the official way is to use the "$loadingKey" property.
It's quite confusing in the documents because there are so many things going on.
https://vue-apollo.netlify.com/guide/apollo/queries.html#loading-state
<template>
<main
v-if="!loading"
class="my-8 mb-4"
>
<div class="w-3/4 mx-auto mb-16">
<h2 class="mx-auto text-4xl text-center heading-underline">
{{ page.title }}
</h2>
<div
class="content"
v-html="page.content.html"
></div>
</div>
</main>
</template>
<script>
import { page } from "~/graphql/page";
export default {
name: 'AboutPage',
data: () => ({
loading: 0
}),
apollo: {
$loadingKey: 'loading',
page: {
query: page,
variables: {
slug: "about"
}
},
}
}
</script>
If you need to use a reactive property within vue such as a slug, you can do so with the following.
<template>
<main
v-if="!loading"
class="my-8 mb-4"
>
<div class="w-3/4 mx-auto mb-16">
<h2 class="mx-auto text-4xl text-center heading-underline">
{{ page.title }}
</h2>
<div
class="content"
v-html="page.content.html"
></div>
</div>
</main>
</template>
<script>
import { page } from "~/graphql/page";
export default {
name: 'AboutPage',
data: () => ({
loading: 0
}),
apollo: {
$loadingKey: 'loading',
page: {
query: page,
variables() {
return {
slug: this.$route.params.slug
}
}
},
}
}
</script>
I think it's only a problem of timing on page load.
You should either iterate on products, if you have more than one, or have a v-if="product != null" on a product container, that will render only once the data is fetched from GraphQL.
In that way you'll use the object in your HTML only when it's really fetched and avoid reading properties from undefined.
To fix this, you add v-if="!$apollo.loading" to the HTML container in which you're taying to use a reactive prop.

Vue.js if else in view

Is it possible to have an if / else statement which does not render any html in a view similar to knockout:
<!-- ko if: someExpressionGoesHere -->
but it needs to be on an element
Yes, but if v-if conditional is false, it's not added to DOM tree.
HTML
<div id="main"></div>
JavaScript
new Vue({
el: "#main",
template: '<div v-if="name"><span v-text="name"></span></div>',
data: {
// name: "bob"
}
});
console.log(document.body.innerHTML);
// <div id="main"><!--vue-if--></div>
Still not good for you?
I know the question was already answered, but thought I would pass along something I use, now that I am writing sites with Vue (which I love.) I am a fan of Knockout and have many sites written in it using the:
<!-- ko if: someExpressionGoesHere -->
You could do a similar thing in Vue like this:
<template v-if="someExpressionGoesHere">
<p>Expression is True</p>
</template>
<template v-else>
<p>Expression is False</p>
</template>
The templates will not render anything to the page. The resulting html will be a single p of the 'Expression is xxx'.
I think it is a bit more clear of what the intent of the code is here than the actual answer to this post IMHO.
you can also use this way to write if else condition in vue.js
<template>
<div id="app">
<p v-if="someConditionHere">Condition True</p>
<p v-else>Condition False</p>
</div>
</template>

angularjs - How to lazy load templates from templateCache in directive

I am trying to display the details of items in a list. This should be done by lazy loading the template (DOM for the details), because the template is very large and i've got many items in the list so a ng-show with ng-include is not working, since it is compiled into the DOM and makes the performance very bad.
After experimenting I figured out a solution, only working with a inline template. I am using a click handler to render the HTML with the detail-view directive to the DOM.
HTML
<div ng-controller="Ctrl">
{{item.name}} <button show-on-click item="item">Show Details</button>
<div class="detailView"></div>
<div ng-include="'include.html'"></div>
</div>
<!-- detailView Template -->
<script type="text/ng-template" id="detailView.html">
<p>With external template: <span>{{details.description}}</span></p>
</script>
Show On Click Directive
myApp.directive("showOnClick", ['$compile', '$parse', function($compile, $parse) {
return {
restrict: 'A',
scope: {
item: "=item"
},
link: function (scope, element, attrs) {
// Bind the click handler
element.bind('click', function() {
// Parse the item
var item = $parse(attrs.item)(scope);
// Find the element to include the details
var next = $(element).next('div.detailView');
// Include and Compile new html with directiv
next.replaceWith($compile('<detail-view details="item"></detail-view>')(scope));
});
}
};
}]);
Detail View Directive:
myApp.directive("detailView", ['$parse', '$templateCache', '$http', function($parse, $templateCache, $http) {
return {
restrict: 'E',
replace: true,
templateUrl: 'detailView.html', // this is not working
// template: "<div>With template in directive: <span>{{details.description}}</span></div>", // uncomment this line to make it work
link: function (scope, element, attrs) {
var item = $parse(attrs.details)(scope);
scope.$apply(function() {
scope.details = item.details;
});
}
};
}]);
Here is the full example on
Plunker
Is there a way to improve my solution, or what am I missing to load the external template?
Thanks beforehand!
You can also look at ng-if directive in Angular version 1.1.5 . ng-if would only render the html if condition is true. So this becomes
<div ng-controller="Ctrl">
{{item.name}} <button ng-if="showDetails" item="item" ng-click='showDetails=true'>Show Details</button>
<div class="detailView"></div>
<div ng-include="'include.html'"></div>
</div>
By just using ng-include:
<div ng-controller="Ctrl" ng-init="detailsViewTemplateSource='';">
{{item.name}}
<button ng-click="detailsViewTemplateSource = 'detailView.html'">
Show Details
</button>
<div ng-include="detailsViewTemplateSource"></div>
</div>
<!-- detailView Template -->
<script type="text/ng-template" id="detailView.html">
<p>With external template: <span>{{details.description}}</span></p>
</script>

Is it possible to pass variables to a mustache partial

Lets say I have a mustache button partial like this
<button name="{{name}}" class="btn">{{title}}</button>
Is it possible somehow to set the title when calling the partial directly like this
<form>
....
{{> button|name=foo,title=Cancel}} {{> button|name=bar,title=Submit}}
</form>
This would make it much easier to create views instead of something like this and creating a hash for each button.
<form>
....
{{#buttons}}
{{> button}}
{{/buttons}}
</form>
I am not sure you can do that but I worked around that using mustache functions
javascript
var partial = "<div>{{value}}</div>";
var data = {some_data:[1,2,3]};
var items = {func:function (){
return function (index){
return mustache.render(partial,{value : data.some_data[index]});
}
});
mustache.render(main,items);
mustache
{{#func}}1{{/func}}
{{#func}}2{{/func}}
It's not possible to do it in plain Mustache, but it's doable in Handlebars with helpers. Handlebars is an extension of Mustache, so you can switch to it just by replacing parser library. Here's how it might look in Handlebars:
JS Helper (there are also implementations for many other languages):
Handlebars.registerHelper('button', function(title, options) {
var attrs = Em.keys(options.hash).map(function(key) {
key + '="' + options.hash[key] + '"';
}).join(" ");
return new Handlebars.SafeString(
"<button " + attrs + ">" + title + "</button>");
});
Usage in template:
{{buttton title name="name" class="class"}}
More info is available here: http://handlebarsjs.com/
No, you cannot pass variable view data directly from the template using Mustache.
However, rendering your buttons as a section using a list is a suitable alternative as of current when using Mustache:
Partial: (buttons.mst)
{{#buttons}}
<button name="{{name}}" class="btn">{{title}}</button>
{{/buttons}}
Template:
<form>
...
{{> buttons}}
</form>
View:
buttons: [
{ name: 'foo', title: 'Cancel' },
{ name: 'bar', title: 'Submit' },
]
Output:
<form>
...
<button name="foo" class="btn">Cancel</button>
<button name="bar" class="btn">Submit</button>
</form>
Which actually works quite well since if no buttons were passed, the partial won't be rendered.
Hope it helps.

Windows 8 JS & html5 app - foreach within template

How do I loop thru items in a WinJS.Binding.Template within my WinJS.UI.ListView control?
My data:
{ category: 'Sports',
items: [
{ title: 'soccer'}, { title: 'tennis'}
]
}
What I want to do in my template:
<div id="myTmpl" data-win-control="WinJS.Binding.Template" style="display:none">
<h1 data-win-bind="innerText: category"></h1>
<div data-win-REPEATER="each: items">
<span data-win-bind="innerText: title"></span>
</div>
</div>
Because you want to nest the details of the items, I would recommend that you look at my answer to another question, which is very similar:
WinJS ListView and Template Binding
This has all the essential details.
That stated, theres no reason you cant integrate JQuerys templates here - you just need to find a way for them to work with the WinJS template / control contract.
Have you checked
http://code.msdn.microsoft.com/windowsapps/ListView-basic-usage-sample-fcc451db
Normally the loop is automatically (it will do based on the datasource information)
<div id="listView"
class="win-selectionstylefilled"
data-win-control="WinJS.UI.ListView"
data-win-options="{
itemDataSource: myData.dataSource,
itemTemplate: myTmpl,
selectionMode: 'none',
tapBehavior: 'none',
swipeBehavior: 'none',
layout: { type: WinJS.UI.GridLayout }
}"
></div>