I have an observable of string Arrays const obs$: Observable<string[]> on my component as a property. While I can successfully use the async pipe on a *ngIf statement, the pipe fails when accessed via array indexer (obs$ | async)[0].
Example:
<!-- evaluates the array emmitted by obs$ for truthyness -->
<div *ngIf="obs$ | async">
<!-- only shown if obs$ emitted an array with length > 0 -->
<!-- but this fails with error: Cannot read property '0' of null -->
<img [src]="(obs$ | async)[0]">
</div>
The instance of obs$ is set in the component's constructor, so obs$ shouldn't be undefined when the template is data-bound.
How to properly access the array's elements in the template?
This might work
<img [src]="(obs$ | async) ? (obs$ | async)[0] : null">
I would try the Elvis operator at this level since your data is undefined at the beginning:
<img [src]="(obs$ | async)?[0]">
Related
I facing such strange warning.
I defined non-Array types look like posted sample codes.
But, Vue3 was throwing warnings.
Have some solutions??
or I got some miss takes?
Simply, I was defined modelValue type look like T | T[] | undefined
But, Vue3 detected Null | Array.
Then, How a T are have been ignored??
I'm using Nuxt3, Vue3, composition api
// types/index.d.ts
type ItemType { [key: string]: any } | string | boolean
// CSelect.vue
<template><div>{{/* contents - It's not a key on this Topic */}}</div></template>
<script setup lang="ts" generic="T extends ItemType">
defineProps<{
items: T[]
modelValue: T | T[] | undefined
}>()
</script>
// create.vue - how i used
<template>
<div class="create">
<div class="title-area">
<CForm>
<InputTemplate>
{{/* This CSelect components are Throw warnings by v-model */}}
<CSelect
v-model="data.category"
class="nft-input"
:items="categoryItems"
/>
</InputTemplate>
</CForm>
</div>
<div>
</template>
<script setup lang="ts">
const categoryItems = computed(() => ['value1', 'value2', 'value3'])
const data = reactive({ category: String(categoryItems.value[0]) })
</script>
[Vue warn]: Invalid prop: type check failed for prop "modelValue". Expected Null | Array, got String with value "Limited".
at <CSelect modelValue="Limited" onUpdate:modelValue=fn class="input" ... >
at <InputTemplate title="Category" >
at <CForm modelValue=false onUpdate:modelValue=fn class="inputs-area" ... >
at <Create onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > >
at <Anonymous key="/member/99/create" routeProps= {Component: {…}, route: {…}} pageKey="/member/99/create" ... >
at <Anonymous >
at <RouterView name=undefined route=undefined >
at <NuxtPage>
at <Default >
at <Anonymous key="default" name="default" hasTransition=false >
at <Anonymous >
at <Anonymous>
at <App key=1 >
at <NuxtRoot>
Your parent component create.vue has no setup and lang="ts". You're passing to a child v-model and class with no : so they aren't props, they are attributes. div element will have them because he is a root element.
Example how to use attributes:
<template>
<div>
<label :for="label">{{label}}</label>
<input v-bind="$attrs" :id="label" :value="$attrs.modelValue" #input="$emit('update:modelValue', $event.target.value)">
</div>
</template>
<script setup>
defineProps(['label'])
defineEmits(['update:modelValue'])
</script>
v-bind assign all attributes to one element.
I'm not using generic in components, so I can just tell you stop! User don't care about generic, and you are not experienced programmer to build UI libraries for different programmers.
I have a web component in my handlebar template that has its own context e.g., this object person{name: "singh"} only exists within <mycompnent></mycomponent>.
I use \{{person.name}} to escape the default handlebar root context and use the local context inside the web component. This works <mycompnent>\{{person.name}}</mycomponent>.
However, I am not able to determine how to use this inside an if expression. My attempts like below all are resulting in error.
<mycompnent>{{#if \person.name }}<p>Good</p>{{/if}}</mycompnent>
<mycompnent>{{#if (\person.name) }}<p>Good</p>{{/if}}</mycompnent>
<mycompnent>{{#if \(person.name) }}<p>Good</p>{{/if}}</mycompnent>
Full code block
<!-- This prints the root -->
{{JSONstringify #root}}
<ul>
{{#each #root.data.items as |item|}}
<li>
<!-- mycomponent exposes a object named person inside the template-->
<mycomponent id="{{item.ID}}">
<template>
<!-- This prints out the item object -->
{{JSONstringify .}}
<!-- This prints out the person object -->
\{{person}}
<!-- This print out the person name if it is set -->
<p>\{{person.displayName}}</p>
<!-- This results in error -->
{{#if (\person.displayName) }}<p>\{{person.displayName}}</p>{{/if}}
</template>
</mycomponent>
</li>
{{/each}}
</ul>
I am trying to use a parameterized segment in a th:each, but I am running into this exception:
EL1007E: Property or field 'author' cannot be found on null
If I got it correct, it means that the object whose variables I'm trying to access is null, though through breakpoint and debug in my Spring MVC app I know for sure there are two elements in the list.
Here is the Controller:
#GetMapping("/")
public String getHomePage(Model model) {
log.info("Recupero la home-page");
model.addAttribute("reviews", mainService.getAllReviews());
return "home";
}
Here is the th:each:
<div
th:each="review: ${reviews}"
th:assert="({review} != null)"
th:replace="fragments/utilities :: test(author=${review.author},message=${review.review})"
></div>
Here is the fragment:
<div th:fragment="test(author, message)">
<p th:text="${message}" class="mt-2 text-dark"></p>
<h6 th:text="${author}"></h6>
</div>
Here is a screenshot of the Model at runtime right before returning the webpage to the client:
What's going wrong? Why does it say review object is null?
There is an issue with your main Thymeleaf template. You need to account on Thymeleaf Attribute Precedence which indicates that th:replacewill be running first, before th:each and replace the entire <div> element. The working code may look similar to ...
<th:block th:each="review: ${reviews}">
<div th:replace="fragments/utilities :: test(author=${review.author},message=${review.review})"></div>
</th:block>
I am working with eXist-db 4.2.1 and Xquery 3.1 using the eXist's default installation of controller.xql and view.xq.
I have a document.html to which I pass any incoming url structured with /doc/some-requested-doc-id at the end to produce a dynamically-created page based on some-requested-doc-id.
So, the incoming url can be either http://localhost:8080/exist/apps/deheresi/doc/MS609-0001 or
http://localhost:8080/exist/apps/deheresi/doc/MS609-0001.xml
and they are treated the same...
In the file controller.xql I have a condition for matching this request, which identifies /doc/ and cleans up the expected some-requested-doc-id using a function which is passed to parameter name="currentdoc":
[...]
else if (starts-with($exist:path, "/doc/")) then
(: strip out any extensions and rename global variable as .xml:)
<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
<forward url="{$exist:controller}/document.html">
<add-parameter name="currentdoc"
value="{concat(functx:substring-before-match($exist:resource,'[.]'),'.xml')}"/>
</forward>
<view>
<forward url="{$exist:controller}/modules/view.xql"/>
</view>
</dispatch>
[...]
The requested .html file is as follows, which itself calls other HTML templates and/or dynamically created content in XQuery:
<div data-template="templates:surround" data-template-with="templates/site_wrapper.html" data-template-at="content">
<div data-template="document:title-bar"/>
<div class="col-sm-12">
<div class="col-md-2 sidebar">
<div data-template="document:doc-sidebar-sub1"/>
<div data-template="document:doc-sidebar-sub2"/>
<div data-template="document:doc-sidebar-sub3"/>
<div data-template="document:doc-sidebar-sub4"/>
</div>
<div class="col-md-10 document-view">
<div data-template="document:doc-xsl-docview"/>
</div>
</div>
</div>
The 5 data-template="document:... calls depend on the same parameter provided by <add-parameter>, for example <div data-template="document:title-bar"/> calls:
declare function document:title-bar(
$node as node(),
$model as map(*),
$currentdoc as xs:string)
{
let $persid := person:person-name(data(doc(concat($globalvar:URIdata,$currentdoc))/tei:TEI/tei:text//tei:persName[#role="dep"]/#nymRef))
let $doctypeen := data(doc(concat($globalvar:URIdata,$currentdoc))/tei:TEI/tei:text//tei:div[#type="doc_type"]/#subtype)
let $x :=
<div class="col-md-12 document-title">
<h2><span class="en">{$doctypeen}: </span><span class="fr">{document:doc-type-french($doctypeen)} : </span>{$persid}</h2>
</div>
return $x
};
Even if I hard-code the parameter in the module controller.xql:
<add-parameter name="currentdoc" value="MS609-00001.xml"/>
I still get the same error, which doesn't happen if I hard code the parameter in the template call:
The actual cardinality for parameter 3 does not match the
cardinality declared in the function's signature:
document:title-bar($node as node(), $model as map,
$currentdoc as xs:string) item()*.
Expected cardinality: exactly one, got 0.
The 'expected cardinality' suggests that the parameter is not coming into the function?
EDIT:
If I change the order of parameters in the function above to
declare function document:title-bar(
$currentdoc as xs:string,
$node as node(),
$model as map(*))
I get a different error:
Supplied argument 2 of function:
document:title-bar($currentdoc as xs:string,
$node as node(), $model as map) item()* does not
match required type. Required type node(), got map. `
Many thanks in advance.
The <add-parameter> directive needs to be moved to the 2nd <forward> directive—so that modules/view.xql has access to the parameter. The corrected version of this fragment of your controller is:
else if (starts-with($exist:path, "/doc/")) then
(: strip out any extensions and rename global variable as .xml:)
<dispatch xmlns="http://exist.sourceforge.net/NS/exist">
<forward url="{$exist:controller}/document.html"/>
<view>
<forward url="{$exist:controller}/modules/view.xql">
<add-parameter name="currentdoc" value="{concat(functx:substring-before-match($exist:resource,'[.]'),'.xml')}"/>
</forward>
</view>
</dispatch>
The templating documentation also shows this - see the 2nd code sample under the "Set up" section here: https://exist-db.org/exist/apps/doc/templating#D3.35.
(There was a mistake in the answer you referenced - which I have now corrected. Apologies, and thanks for your thorough testing and well-articulated questions!)
I want to be able to show a list of all the FirstName and ZipCodes. My data looks as follows:
/user
|
|__INAxzxWKQrSAfA7tapV0c08YvfJ3
| |____FirstName:"James"
| |____ZipCode:"90210"
|
|__ANAczxWKQrEAfA7tapV0c08YvfX6
|____FirstName:"Simon"
|____ZipCode:"40213"
and Polymerfire's firebase document looks like this
<firebase-document
app-name="contacts"
path="/user"
data="{{allUsers}}">
</firebase-document>
And my dom repeat is like
<template is="dom-repeat" items="{{_makeArray(allUsers)}}">
<div class="profile card">
{{item.FirstName}}
</div>
</template>
I added the function
_makeArray: function(items) {
return Object.keys(items).map(function (key) {items[key]});
}
There are no errors but I also get nothing to the DOM
just use firebase-query instead of firebase-document. you'll get an array back.
<firebase-query
app-name="contacts"
path="/user"
data="{{allUsers}}">
</firebase-query>
<template is="dom-repeat" items="{{allUsers)}}">
<div class="profile card">
{{item.FirstName}}
</div>
</template>
dom-repeat takes an array as the items property. You can do something like this:
<template is="dom-repeat" items="[[makeArray(allUsers)]]">
...
makeArray: function(items) {
return Object.keys(items).map(function (key) {items[key]});
}
You forgot the return:
_makeArray: function(items) {
return Object.keys(items).map(function (key) { return items[key]; });
}
Its works fine with me data are displayed just fine.
Better implementation (also includes the name of the key if required) can be found here:
How to use dom-repeat with objects instead of arrays in Polymer 1.0?
Also an implementation for two way binding:
Two way binding for Firebase arrays in dom-repeat
Hope this gets implemented in Polymer 2.0, as Firebase is being advertised but then there are problems with using best practice Polymer and best practice Firebase together.
Use firebase-query to return an array of object that you can pass to dom-repeate.
<firebase-query
path="/user"
data="{{allUsers}}"
</firebase-query>
The dom-repeat
add attribute as="data"
add property data.$key
<template is="dom-repeat" items="{{_makeArray(allUsers)}}" as="data">
<div class="profile card">
{{data.$key}}
{{data.FirstName}}
</div>
</template>