I'm somewhat (6 months) new to MVC, and I like to use as little as code as possible, especially when it comes to reusable code, etc. I'm using Umbraco v7.2, and I have (3) tabs, all which use the same data type (custom grid v7).
The grid has (4) fields. Basically all (3) sections on my page are the same w/ the exception for the header and the object that is called (the dynamic object is what has the properties in them for the tab, which as I stated earlier, are the same).
How can I call a partial view and reuse the same code? The "foreach" is where I need to have this partial view called, as you can see it uses the same exact code w/ the exception of the object being iterated.
The "CurrentPage.XXXX" is what I need to pass, and I can have the same iterator
#foreach(var XXXX in CurrentPage.XXXX) <---- partial view
View:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
Layout = "Master.cshtml";
}
<article class="accordion-wrapper">
<div class="accordion-container accordion-contact">
Leadership Team
#foreach (var leadership in CurrentPage.leadershipTeam)
{
<section class="clearfix">
<div class="col-md-6 col-sm-6">
<ul>
<li>#leadership.contactName</li>
<li>#leadership.contactTitle</li>
</ul>
</div>
<aside class="col-md-6 col-sm-6">
<ul>
<li>#leadership.contactPhone</li>
<li>
#leadership.contactEmail
</li>
</ul>
</aside>
</section>
}
</div>
</article>
<article class="accordion-wrapper">
<div class="accordion-container accordion-contact">
The Lenders One Team
#foreach (var lenders in CurrentPage.lendersTeam)
{
<section class="clearfix">
<div class="col-md-6 col-sm-6">
<ul>
<li>#lenders.contactName</li>
<li>#lenders.contactTitle</li>
</ul>
</div>
<aside class="col-md-6 col-sm-6">
<ul>
<li>#lenders.contactPhone</li>
<li>
#lenders.contactEmail
</li>
</ul>
</aside>
</section>
}
</div>
</article>
.... another one here but omitted for brevity
And turn it into:
<article class="accordion-wrapper">
<div class="accordion-container accordion-contact">
Leadership Team
#Html.Partial( ?????? )
</div>
</article>
<article class="accordion-wrapper">
<div class="accordion-container accordion-contact">
The Lenders One Team
#Html.Partial( ?????? )
</div>
</article>
Partial ???
#foreach (var contact in ??????)
{
<section class="clearfix">
<div class="col-md-6 col-sm-6">
<ul>
<li>#contact.contactName</li>
<li>#contact.contactTitle</li>
</ul>
</div>
<aside class="col-md-6 col-sm-6">
<ul>
<li>#contact.contactPhone</li>
<li>
#contact.contactEmail
</li>
</ul>
</aside>
</section>
}
Appreciate it ;)
EDIT:
To clarify, I've used partial views before in Umbraco. The issue above is I have (3) different objects (grids in u7). How the grid works in Umbraco is you create a new data type, and define certain fields in that data type (textbox, media picker, etc). You can then add properties to document types (in this case I used the custom grid I created). Once a page is created, based off a document type, properties are inherited.
For the contact page, I needed (3) separate grids. However each grid has different data in them. Therefore this is (3) different JSON objects, which I iterate over. In the above code, the (3) JSON objects are:
leadershipTeam
lendersTeam
avisoryBoard (the one omitted for
brevity)
How can I pass (CurrentPage.JSONobjectHere) to the partial view, using only ONE partial view for all THREE sections?
Did something similar to this once.
Call your partial like this:
#Html.Partial("YourPartialName", (object)CurrentPage.lendersTeam)
And then use a dynamic as a model in your partial:
#model dynamic
#foreach (var contact in Model)
{
<section class="clearfix">
<div class="col-md-6 col-sm-6">
<ul>
<li>#contact.contactName</li>
<li>#contact.contactTitle</li>
</ul>
</div>
<aside class="col-md-6 col-sm-6">
<ul>
<li>#contact.contactPhone</li>
<li>
#contact.contactEmail
</li>
</ul>
</aside>
</section>
}
This is not too difficult. In the call to the partial, just use the name of the partial. This is the filename of the partial without the extention. The current "Model" will also be available in your partial without you have to pass something to the partial.
<article class="accordion-wrapper">
<div class="accordion-container accordion-contact">
Leadership Team
#Html.Partial("NameOfThePartialWithoutExtention")
</div>
</article>
If you inherit from UmbracoTemplatePage or the UmbracoViewPage, then you can use the model as if you were in the View itself.
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#foreach (var contact in CurrentPage.Children)
{
<section class="clearfix">
<div class="col-md-6 col-sm-6">
<ul>
<li>#contact.contactName</li>
<li>#contact.contactTitle</li>
</ul>
</div>
</section>
}
Thanks Morten OC. Thumbs up. I was using NestedContent and calling Partial view to render IPublishedContent, but also wanted access to CurrentPage.
Simple cast to object first worked. To elaborate, here's some extra code in case someone needs the same -
Html.RenderPartial("~/Views/Partials/MyPartial.cshtml",
item, //for me this is an IPublishedContent
new ViewDataDictionary { { "CurrentPage", (object)CurrentPage } });
Then in my Partial, shortened for brevity -
#inherits Umbraco.Web.Mvc.UmbracoViewPage<IPublishedContent>
#{
dynamic CurrentPage = ViewBag.CurrentPage;
}
<p>#CurrentPage.Name</p>
So my Model is IPublishedContent (since I was iterating through nested contents)
And then in ViewBag you'll have a reference to CurrentPage. I named it CurrentPage just so I could continue using CurrentPage as we typically do in Umbraco.
Cheers Morten H5YR
Related
I'm currently trying to create an index of cards depicting rooms, with their associated desks indexed within them, using nested foreach loops within the view:
<div class="grid grid-cols-1 gap-3 ">
#foreach ($rooms as $room)
<div class="bg-white text-center h-auto">
{{ $room->name }}
<div class="m-3">
<div class="lg:grid lg:grid-cols-6 gap-3">
#foreach ($desks as $desk)
<div class="bg-red-200 text-center h-10">
Desk {{ $desk->id }}
</div>
#endforeach
</div>
</div>
</div>
#endforeach
</div>
The Room controller index action currently looks like this:
class RoomController extends Controller
{
public function index()
{
return view('index', [
'rooms' => Room::all(),
'desks' => Desk::all()
]);
}
}
I'm aware that Desk::all() is indexing all desks for all rooms. Instead, I need to build a query that indexes desks belonging only to each room. My model relationships are defined as:
Room hasMany desks
Desk belongsTo room
Any help would be much appreciated, thanks.
instant doing like rooms::all and desk::all use can simply run an eloquent query like:-
room::with("desks")->get();
note :-you need to make a relation in your room model with name "desks" like this :-
public function desks(){
return $this->hasMany(desk::class);
}
and your foreign key name must be like model name +_id
Managed to solve this. Desk::all() worked as I wanted to make all desks available at controller level. Instead I added a where clause to the nested foreach loop to filter by room_id:
<div class="grid grid-cols-1 gap-3 ">
#foreach ($rooms as $room)
<div class="bg-white text-center h-auto">
{{ $room->name }}
<div class="m-3">
<div class="lg:grid lg:grid-cols-6 gap-3">
#foreach ($desks->where('room_id', $room->id) as $desk)
<div class="bg-red-200 text-center h-10">
Desk {{ $desk->id }}
</div>
#endforeach
</div>
</div>
</div>
I am trying to learn the new feature in ASP.NET Blazor. I am using Visual Studio 2019. I am trying to create an Ideas Registration form. So the code for dropdownlist i had took from Bootstrap 4. It was not working as expected. Can you please tell me where i am working wrong?
Just a little overwhelmed here, any advice would be much appreciated.
Given Code:
<!-- Card Body -->
<div class="card-body">
<!-- <form -->
<form>
<div class="form-group">
<label for="exampleFormControlInput1">Title</label>
<input type="email" class="form-control" id="exampleFormControlInput1">
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Description</label>
<textarea class="form-control" id="exampleFormControlTextarea1" rows="4"></textarea>
</div>
<!-- Basic dropdown -->
<div class="form-group">
<button class="btn btn-primary dropdown-toggle mr-4" type="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
Basic dropdown
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">.Net</a>
<a class="dropdown-item" href="#">Python</a>
<a class="dropdown-item" href="#">Data Science</a>
<div class="dropdown-divider"></div>
</div>
</div>
<!-- Basic dropdown -->
where i am working wrong
According to the official docs](https://getbootstrap.com/docs/4.0/components/dropdowns/#data-toggledropdown-still-required):
Regardless of whether you call your dropdown via JavaScript or instead use the data-api, data-toggle="dropdown" is always required to be present on the dropdown’s trigger element.
I would suggest you should wrap your Basic dropdown in the following structure
<div class="dropdown">
<button data-toggle="dropdown" class="..." > ...</button>
<div class="dropdown-menu ...>
...
</div>
</div>
You didn't add an event handler for selection. At least you should add a #onclick for the toggle button. When clicking this button, show or hide the dropdown-menu.
Finally, if you want to implement the dropdown component with Blazor(without javascript), you should also replace the text content within the toggle button when someone selects a dropdown list item.
A Demo : How to Create A General Dropdown Component
Rather than simply fixing the issue, I think it's much better to create a general dropdown Component so that we can always invoke them in following way:
#{ var list = new List<string>{ ".NET", "Python","Java" }; }
<Dropdown TItem="string" OnSelected="#OnSelected" >
<InitialTip>This is a dropdown list</InitialTip>
<ChildContent>
<DropdownListItem Item="#list[0]">.NET</DropdownListItem>
<DropdownListItem Item="#list[1]">Python</DropdownListItem>
<div class="dropdown-divider"></div>
<DropdownListItem Item="#list[2]">Java</DropdownListItem>
</ChildContent>
</Dropdown>
#code {
private void OnSelected(string selection)
{
Console.WriteLine(selection);
}
}
Here the TItem is a generic type parameter that is the type of each dropdown list item and can be any .NET type.
Demo
How-To
Add a Shared/Dropdown.razor component:
#using Microsoft.AspNetCore.Components.Web
#typeparam TItem
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle mr-4" data-toggle="dropdown" type="button" #onclick="e => this.show=!this.show "
aria-haspopup="true" aria-expanded="false">
#Tip
</button>
<CascadingValue name="Dropdown" Value="#this">
<div class="dropdown-menu #(show? "show":"")" >
#ChildContent
</div>
</CascadingValue>
</div>
#code {
[Parameter]
public RenderFragment InitialTip{get;set;}
[Parameter]
public RenderFragment ChildContent{get;set;}
[Parameter]
public EventCallback<TItem> OnSelected {get;set;}
private bool show = false;
private RenderFragment Tip ;
protected override void OnInitialized(){ this.Tip = InitialTip; }
public async Task HandleSelect(TItem item, RenderFragment<TItem> contentFragment)
{
this.Tip= contentFragment.Invoke(item);
this.show=false;
StateHasChanged();
await this.OnSelected.InvokeAsync(item);
}
}
Add a Shared/DropdownListItem.razor component:
#using Microsoft.AspNetCore.Components.Web
#typeparam TItem
<a class="dropdown-item" Item="#Item" #onclick="e=> Dropdown.HandleSelect(Item, ChildContent)" >#ChildContent(Item)</a>
#code {
[CascadingParameter(Name="Dropdown")]
public Dropdown<TItem> Dropdown {get;set;}
[Parameter]
public TItem Item{get;set;}
[Parameter]
public RenderFragment<TItem> ChildContent {get;set;}
}
Keep in mind that bootstrap dropdown requires bootstrap javascript to be referenced. And the Blazor template doesn't reference it by default.
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
The accepted answer is great. However, as many have pointed out, the dropdown does not close if an option is not selected. The suggestion to create an #unblur event does not solve the case, as #unclick and #unblur do not seem to work together in .NET 5 (I read somewhere that it does works in the new .NET 6) - #unblur prevents #unclick to be triggered.
I found this solution (#onblur prevents #onclick in blazor server side app), changing the #onclick to #onmousedown and then creating the #onblur event (as suggested) has fixed the issue for me.
<li class="tabRow tabRowLeft" *ngFor="let gene of filteredgene = (seq.genes) | limitTo:filteredgene.length/2+filteredgene.length%2">
<div class="displayFlex" (click)="showGeneRecord(gene.geneName,'ATTRIBUTE','.addPopup.attributeRisk')">
<div class="tabCell">
<div class="cellItem displayFlex">
<h4 class="flex1">{{gene.geneName}}</h4>
</div>
</div>
<div class="tabCell">
<div class="cellItem displayFlex">
<h4 class="flex1">{{gene.geneScore}}</h4>
</div>
</div>
</div>
</li>
I am trying to get the value in filteredgene and then for loop on filteredgene . I am getting error Bindings cannot contain assignments. Any one knows, what should I do resolve and get this thing done.
For limit to I have created a pipe too.
I am trying to bind data-tracking-label="plan.priceplan_name".
Not sure how do i go about binding the priceplan_name dynamically to the data-tracking-label.
Already tried a few options including:
data-tracking-label={{plan.priceplan_name}} - which has error :
Uncaught Error: Template parse errors:
Can't bind to 'tracking-label' since it isn't a known property of 'div'.
Below is a snippet of my code.
<div *ngFor="let plan of plans; let i = index" class="col-md-3 pt-2 pb-2 pl-2 pr-2">
<div class="card">
<div class="card-body row p-0" (click)="selectPlan(plan, i)" data-tracking-category="SEA - Choose-plan" data-tracking-action="Click" data-tracking-label={{plan.priceplan_name}}>
<div class="first-sec col-6 pt-2 pb-2 rounded-left">
<h3>{{plan.priceplan_name}}</h3>
<div class="position-bottom">
<h2 class="mb-0">R{{plan.base_priceplan_cost | number:'1.0-2'}}pm</h2>
<h5>x{{plan.contract_duration}}</h5>
</div>
</div>
</div>
</div>
</div>
Here is another way to add the tags which is being used.
Note that a service with a windowRef provider was added to the app.module.ts and imported in every controller that makes use of the dataLayer.
(click)="myFunstion(param); dataLayer.push({event:'MyEvent', category:'MyCategory', action:'Click', label:plan.priceplan_name})
And in the controller:
import { WindowRef } from '../WindowRef';
Declare var before constructor:
dataLayer = this.winRef.nativeWindow.dataLayer;
And add variable inside constructor:
private winRef: WindowRef
I have an anchor which should replace a grid with a partial view .
<a class="btn btn-primary"
data-ajax="true"
data-ajax-method="GET"
data-ajax-mode="replace"
data-ajax-update="content"
data-ajax-url="#Url.Action("add","user")"> Create User </a>
<div class="row table-area">
<div class="col-md-12" id="content">
#Html.AjaxGrid(Url.Action("results", "user"))
</div>
</div>
I see it calls the user action with partial view but it never updates the section with id="content".
Here is my controller method -
[Route("add")]
public IActionResult AddUser()
{
return PartialView("Partials/AddUser",new RegisterViewModel());
}
Ideally it should replace the grid content with the partial view altogether but it is not replacing . The response status is 200 and I can see that the contents are being returned in response . Anybody has any idea what is the issue here ?
Change data-ajax-update="content" to data-ajax-update="#content"
Rather than using data-ajax-url, use the asp-controller and asp-action and the #content should work.
<a class="btn btn-primary" asp-controller="user" asp-action="add"
data-ajax="true"
data-ajax-method="GET"
data-ajax-mode="replace"
data-ajax-update="#content">Create User</a>