I'm newbie to Aurelia framework, a common and popular scenario: I have a container (view-model and view) that is populated by a collection of items (lets call it users accounts screen, that should contains a collection of user accounts).
The container uses a service that produce a collection of the users accounts data.
I have a view and a view-model which are bound together and represent a single user account - I would like the container use the user account view model by sending it a parameter (user account data) - the user account will receive the data and set it to its properties, so it can be bound to the view.
The purpose is that the user account view model will receive the data either in the constructor or in the activate (I think that this is preferred).
You can accomplish this by using simple databinding. The bound data would be available for binding in the component.
Here's an example: https://gist.run?id=a8f11e779253a1c6abb44dbec77a8b83
account-info.html
<template>
Hello ${data.name}!
</template>
account-info.js
import {bindable} from 'aurelia-framework';
export class AccountInfo {
#bindable data;
}
app.html
<template>
<require from="./account-info"></require>
<div><input type="text" value.bind="userData.name" /></div>
<account-info data.bind="userData"></account-info>
</template>
app.js
export class App {
userData = {
name: 'Ashley'
}
}
I solved this by using the model.bind property in the compose element.
Related
I'm currently developing a small Ionic 4 project for college that uses Google Firebase to save and store data, which will be used by the nursing course / class to make things easier for them when it comes to saving patients' data and test details.
In the page that shows all registered patients, I have an ion-searchbar which will be used to filter the patients by name and a ng-container with a *ngFor that pulls all registered patients from the database and puts them into ion-cards.
<ion-searchbar placeholder="Search..." expand="full" color="abwb" [(ngModel)]="searchQuery" (ionChange)="search()"></ion-searchbar>
<ng-container *ngFor="let patient of dBase">
<ion-card color="abwb">
<ion-card-header style="font-weight: bold">{{patient.name}}</ion-card-header>
<ion-card-content>
<ion-button [routerLink]="['/pat-prf', patient.id]" color="bwab">PROFILE<ion-icon name="person" slot="start"></ion-icon></ion-button>
<ion-button [routerLink]="['/pat-tst', patient.id]" color="bwab">TESTS<ion-icon name="list-box" slot="start"></ion-icon></ion-button>
</ion-card-content>
</ion-card>
</ng-container>
I managed to get the value from what's being typed into the search field to be displayed via a console.log() action, but I have no idea on how to make it actually go and search the database for the specified names OR to make the ng-container only show the cards with names that match.
I've been told I had to use a *ngIf to do such a thing, but I honestly got no idea how to use it properly for this kind of thing. Can anyone point me in the right direction?
You have two solutions, a Frontend solution and a Backend solution.
The Frontend solution
The frontend is to filter out the list that you have from the server. This is can use the Array.prototype.filter(). In order to make this work, the backend should return all the patients without pagination (Which can work for small list, but not preferred for big one).
// allPatients will contain all the results from the server and dBase will be filtered
private allPatients = [];
ngOnInit() {
this.httpClient.get(MY_API_TO_GET_PATIENTS)
.subscribe(response => this.allPatients = this.dBase = response )
}
search() {
this.dBase = allPatients.filter(item => item.name.includes(this.searchQuery));
}
The Backend solution
In this solution, we will send a request to the Backend that contains the search query. Then, the Backend will respond with the results that met the search query. Here we will need only to send a new request to the server each time the user enters a new value.
search() {
this.httpClient.get(MY_URL, {params: {searchQuery: this.searchQuery})
.subscribe(response => this.dBase = response )
}
Background
I am trying to make a dynamic navigation component in AEM. Currently I have been trying to find information in the docs and online to help me but have not been able to find much.
My application loads vue components into AEM so the front end is controlled by VUE and the data is passed in through props.
I would like to pass the pages and sub pages of those pages into the vue component, so I can create a dynamic navigation with a drop down nav for any links.
Example
Currently I found a way to use a java class to load the root pages links. I am doing that like this,
package services.agro.something.core.models;
import java.util.*;
import java.util.Iterator;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageFilter;
import com.adobe.cq.sightly.WCMUsePojo;
public class SidebarNavigationModel extends WCMUsePojo{
private List<Page> items = new ArrayList<Page>();
private Page rootPage;
// Initializes the navigation
#Override
public void activate() throws Exception {
rootPage = getCurrentPage().getAbsoluteParent(2);
if (rootPage == null) {
rootPage = getCurrentPage();
}
Iterator<Page> childPages = rootPage.listChildren(new PageFilter(getRequest()));
while (childPages.hasNext()) {
items.add(childPages.next());
}
}
// Returns the navigation items
public List<Page> getItems() {
return items;
}
// Returns the navigation root
public Page getRoot() {
return rootPage;
}
}
Then I could use this data like this,
<div data-sly-use.sidebarNavigationModel="services.agro.gde.core.models.SidebarNavigationModel" class="sidebar-navigation ae-spacing-indent">
<div class="sidebar-nav-container">
// This is a vue component that I am trying to pass data to. The data comes through as commas and when I expect an array an error throws in console stating it expects array but receives a string.
<sidebar-navigation title="${properties.title}" items="${sidebarNavigationModel.items}"></sidebar-navigation>
</div>
// This outputs 16 commas to the screen ${sidebarNavigationModel.items}
<ul class="nav navbar-nav navbar-center">
// This outputs a link to home
<li>HOME</li>
// This outputs 19 links like I expect
<li class="nav navbar-nav navbar-left" data-sly-repeat="${sidebarNavigationModel.items}">
${item.title}
</li>
</ul>
</div>
Question
If I could better understand the data set that is returned in the loop I believe I could pass it to the vue component as I have tried.
Why does this output commas when outside of the loop and why when passing it to vue does it appears to be a string?
How can I get the sub pages for a site when it loads into a data set I can pass to my vue component from the template?
HTL/Sightly is trying to render the array but for each item it will render nothing (since it cannot evaluate and render the Page object). What you could do is either massage the data in your Use-Object to return exactly the needed string or process it in the script like:
[<sly data-sly-repeat="${logic.items}">{title="${item.title}",path="${sidebarNavigationModel.path}"}${itemList.last?'':','}</sly>]
The vue component is printing commas because the list returned by java class is a list of com.day.cq.wcm.api.Page objects and not a list of String. Sightly understands that it is a Page object and hence able to invoke page methods on it to retrieve path and title.
Getting a list of subpages - There are different ways you can do this. Depends a lot on your content structure and your requirement - what you consider are child and parent pages.
You can set an attribute in page properties for all the parents pages and use this attribute to identify a parent page and then construct a map of parent - child pages.
Or if your content structure runs more than a level deep and you need child pages from deep down the heirarchy, you can recursively fetch all the child pages and format into a data structure your vue component understands.
Or you could use Page API's like hasChild(), listChildren(), etc to drive your logic of constructing parent-child nav structure.
All this can be achieved from your java class.
I have a search form with three fields: Department, Role and Location which I'm calling Contacts search and it is to be on multiple pages.
Here is a picture:
To this extent, I made it a Blade include so I could just re-use it wherever I need it.
All of the values for these fields are available to me in the Users table as I could do the following:
SELECT DISTINCT department, role, location FROM users
This would give me all of the unique roles, departments and locations.
However, for me to populate this form via the database would I have to pass the data to the parent view every time?
For instance, I was thinking something like this.
In Controller
$formFields = User::distinct()->get(['department', 'role', 'location']);
In view
#foreach(formFields as $field)
$field->department
...
...
#endforeach
The only issue is, surely I would have to send this data to every view that uses the form?
Alternatively, do I just make a SearchController that uses the User model to feed into it?
Is it possible to feed data to a partial view in this way?
Update
View composers do seem to be the way to go, however I don't feel this is valid:
view()->composer('layouts.quick-search', function($view) {
$view->with('departments', \App\User::distinct()->get(['department']),
'locations', \App\User::distinct()->get(['location']),
'roles', \App\User::distinct()->get(['role']));
});
As it doesn't seem to like multiple variables.
I also tried the following:
<select class="form-control transparant" id="department" name="department">
#foreach($departments as $department)
<option value="{{ $department }}">{{ $department }}</option>
#endforeach
</select>
But this filled the select box with:
{"department":"Digital","profile":null}
This is clearly an array, but does this mean I'll have to JSON decode this?
As I mentioned in my comment, you could do something like this:
In view
#php $formFields = \App\User::distinct()->get(['department', 'role', 'location']); #endphp
#foreach(formFields as $field)
$field->department
...
...
#endforeach
Another way to do this would be creating a group of all those views that use the partial view. Apply a middleware to that group where you assign the data to the request or share the data to the views using View::share()
View composers are perfect for this.
You create a separate view for the form, something like search and put this in a service provider:
use Illuminate\Support\Facades\View;
use App\User;
// ...
publlic function boot()
{
View::composer('search', function ($view) {
$view->with([
'formFields' => User::distinct()->get(['department', 'role', 'location']);
]);
});
}
Now every time the search view will be used, this query will be executed and the results will be passed to the view.
I'm a newbie with Angular2 (beta1) and I'd like to implement a sort of simple editable grid, built of 2 components. Here I use two fake-data components to keep things simple. They are (see this Plunker: http://plnkr.co/edit/5cZfLTIlhLc82wWV4PQI):
the parent component, named contact. Say it represents a contact with a name.
the child component, named entry. Say it represents an entry for a contact, where each contact can include 0 or more entries. Each entry has an address and a zip code.
I'd like to create a form where the user can edit the contact's properties, and also its children entries: he could add a new entry, delete an existing entry, or edit an existing entry.
To this end, the views for both these components provide a form-based template.
I can think of this data flow:
contact: the user edits the form and then clicks a submit button to save
the whole thing. Thus, I can just have some code handling the submit button
and emitting an event as the component output. The contact has an entries
array property: I can thus use an ngFor directive in its template to render
an entry component for each of them.
entry: the entry has properties addressCtl and zipCtl which represent
the control directives included in the ControlGroup representing the whole
form. Also, I need a couple of properties to be bound as the input of the
component (address and zip), so that in the parent template I can do something like:
<tr *ngFor="#e of entries">
<td><my-entry [address]="e.address" [zip]="e.zip"></my-entry></td>
</tr>
Now, it's not clear to me how to shape the relation between the "model" properties representing the control's input, and the "form" directives properties. I should be able to get the address and zip values from the parent component through the [...] binding, and pass the updated values up through an event fired by the child component (e.g. blur?). Does this make sense in the NG2 world? Anyway, I'm missing a piece here: how can I connect the form controls values to the model properties values? Could anyone make this clearer or point to some good docs?
In fact, using the [...] binding only corresponds to a one-way binding. When the parent property is updated in the parent component, the value is also updated in the child component.
But if you want to update parent attributes from the child, you need to leverage events and #Ouput attribute.
Here is a sample with a labels component:
export class LabelsComponent implements OnInit {
#Input()
labels:string[];
#Output()
labelsChange: EventEmitter;
(...)
removeLabel(label:string) {
var index = this.labels.indexOf(label, 0);
if (index != undefined) {
this.labels.splice(index, 1);
this.labelsChange.emit(this.labels);
}
}
addLabel(label:string) {
this.labels.push(this.labelToAdd);
this.labelsChange.emit(this.labels);
this.labelToAdd = '';
this.addAreaDisplayed = false;
}
}
This way you can leverage two way binding on this component:
<labels [(labels)]="company.labels"></labels>
Hope it answers your question,
Thierry
Just moved the comment to answer...
You can pass the object e, instead of passing string.
i.e
<my-entry [entry] = "e"></my-entry>
then in your my-entry component, use ng-model for each input. so you automatically gets 2 way bindings.
I'm trying to make a simple extension of the table element. Where you can click a td, then it becomes editable, and when you edit the data it gets automatically persisted via a REST service.
Here's what I got so far
As you can see, you can click the td's and edit them, but the data does not get persisted to the other side (which is firebase in this case). That's because the data in the td's aren't bound anymore to the data-property from which they came. Can somebody tell me how I can bind them to that property again? Or any other way I can persist the data to the correct row and key?
As far as I know contenteditable change events are not supported by polymer.
You could use the onkeys to update the model manually.
In a on-* handler, you can access the named model instance using: e.target.templateInstance.model.:
<polymer-element name="x-foo">
<template>
<template repeat="{{user in users}}">
<div on-click="{{clickHandler}}">{{user.name}}</div>
</template>
</template>
<script>
Polymer('x-foo', {
clickHandler: function(e, detail, sender) {
console.log(sender.templateInstance.model.user.name);
}
});
</script>
</polymer-element>
Sevesta told me that it could only be done manually, so I gave every td extra data-attributes so I could identify them and then at the stopEditing() function I update the models manually.
See here.