I am using this awesome library for drag and drop functionality.
Dragula is very good at drag and drop.
One thing I am trying to do is to disable dragging to reorder in own container. But should allow dragging if going to drop in a connected/linked container.
for example following two div tags as containers
<div dragula="dropContainer" id="dropbag1" [(dragulaModel)]="bagOneModel">
<div *ngFor="let model of bagOneModel" class="col-sm-2 col-md-2 col-lg-2">
{{model}}
</div>
</div>
<div dragula="dropContainer" id="dropbag2" [(dragulaModel)]="bag2Model">
<div *ngFor="let model of bag2Model" class="col-sm-2 col-md-2 col-lg-2">
{{model}}
<!-- don't allow re ordering in this container -->
</div>
</div>
It's very easy to create something that permit to drag:
from A to B
from B to A
and disable
reorder inside A
reorder inside B
In your name.component.ts you should add:
constructor(public dragulaService: DragulaService) {
dragulaService.createGroup('dropContainer', {
accepts: (el, target, source, sibling): boolean => {
if (!target || !source || (target === source)) {
return false;
}
return true;
}
});
}
Related
From code template we have:
<div class="block #block.CssName()">
<div class="container">
#Html.DisplayFor(m => block, block.GetType().Name)
</div>
</div>
it make my content block always inside container class. How to make a flexible page where we can put a block inside and outside container
You can check the block type and put a different class on the div besides "container" like this:
<!-- language: lang-razor -->
<div class="block #block.CssName()">
<div class="#(block.GetType().Name == "MySpecialBlock" ? "myspecialclass" : "container") ">
#Html.DisplayFor(m => block, block.GetType().Name)
</div>
</div>
or you can remove the div altogether:
<!-- language: lang-razor -->
<div class="block #block.CssName()">
#Html.DisplayFor(m => block, block.GetType().Name)
</div>
with the intention of editing each DisplayTemplate .cshtml file and add a wrapper div there. i.e. /Views/Cms/DisplayTemplates/HtmlBlock.cshtml :
<div class="myWrapperClass">
#Html.Raw(Model)
</div>
Note: If you did this, you'd probably want to edit each of the various block type templates to add a wrapper of some kind.
Another possibility would be to write a helper class that checks the block type and automatically returns a specific class depending on each block type.
using Piranha.Extend;
namespace MyProject.Classes
{
public static class Helper
{
public static string getWrapperCssClassForBlockType (Block block)
{
string blockName = block.GetType().Name;
string className = "";
switch(blockName)
{
case "HtmlBlock":
className = "row";
break;
case "QuoteBlock":
className = "myQuoteClass";
break;
default:
className = "container";
break;
}
return className;
}
}
}
Then just call the helper method from in your template(s):
#using MyProject.Classes;
#foreach (var block in Model.Blocks)
{
var wrapperClass = Helper.getWrapperCssClassForBlockType(block);
<div class="block #block.CssName()">
<div class="#wrapperClass">
#Html.DisplayFor(m => block, block.GetType().Name)
</div>
</div>
}
So finally i've made it by removing containerfrom template:
<div class="block #block.CssName()">
<!--<div class="container">-->
#Html.DisplayFor(m => block, block.GetType().Name)
<!--</div>-->
</div>
It make all standard block sit outside container. Next i made a group block of container. Any block can be inside this container group block except standard group block such as gallery and columns. Luckily it is open source, so we can make our own columns and gallery group block to be inside a container by modify cshtml template. I dont know how piranha.org do this
But i think my solve fit my purpose
I have a list of "workbooks" displayed in a table. Each workbook has a "Share" button next to the workbook's title. When the user clicks on the share button a modal dialog is shown containing a form.
The form allows the user to enter a list of the recipient's emails separated by a comma which is validated on the client-side.
As the dialog is located in a partial view _ShareView.cshtml that allows me to pass a modal WorkbookShareModel that has some fields like WorkbookId and Title. The goal here is to pass the details of each workbook when the user presses the share button (i.e. construct a modal and pass it to the already rendered model).
I am not sure how to pass a model to an already rendered view?
The solution have to be done on the client (i.e. dont involve actions on the server that return the partial view provided the parameters are passed). I want to avoid unnesessary calls to the server - we have all the data on the client regarding a workbook and I need to do a POST when the user types in list of emails.
This is my index.cshtml:
#section BodyFill
{
<div id="shareFormContainer">
#{ await Html.RenderPartialAsync("_ShareView", new WorkbookShareModel());}
</div>
<div class="landing-container">
<div class="workbook-container">
<table class="table">
<tbody>
#foreach (var workbook in Model.Workbooks)
{
string trClassName, linkText;
if (workbook.Metadata.SharedBy == null)
{
trClassName = "saved-workbooks";
linkText = workbook.Name;
} else {
trClassName = "shared-with-me";
linkText = string.Format(
BaseLanguage.SharedWithMeWorkbook,
workbook.Name,
workbook.Metadata.SharedBy,
workbook.Metadata.SharedDate.ToShortDateString()
);
}
<tr class="#trClassName">
<td>#Html.ActionLink(linkText, "Open", "OpenAnalytics", new { id = Model.Id, workbook = workbook.Name })</td>
<td class="last-modified-date" title="Last Modified Date">#workbook.ModifiedDate.ToShortDateString()</td>
<td class="share">
<button title="Share" class="share-button" onclick='showSharingView("#workbook.Name", "#workbook.Id", "#Model.Id")'> </button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
#section Scripts
{
<!--Load JQuery 'unobtrusive' validation -->
#await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript">
// hide the modal as soon as the page loads
$('#shareFormModal').modal("hide");
function showSharingView(title, workbookId, id) {
$('#shareFormModal').modal("show");
// how to pass a WorkbookShareModel to my partial view from here?
}
function hideDialog() {
var form = $("#partialform");
// only hide the dialog if the form is valid
if (form.valid()) {
activateShareButtons();
$('#shareFormModal').modal("hide");
}
}
// Helper method that validates list of emails
function IsEmailValid(emailList, element, parameters) {
var SPLIT_REGEXP = /[,;\s]\s*/;
var EMAIL_REGEXP =
/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+##[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/i;
var emails = emailList.split(SPLIT_REGEXP);
for (var i = emails.length; i--;) {
if (!EMAIL_REGEXP.test(emails[i].trim())) {
return false;
}
}
return true;
}
</script>
}
That is my dialog:
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.WorkbookShareModel
#* Partial view that contains the 'Share Workbook dialog' modal *#
<!-- Modal -->
<div onclick="activateShareButtons()" class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new { #id = "partialform" }))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title" />
<input asp-for="Id" />
<input asp-for="WorkbookId"/>
</div>
<div class="modal-footer">
<button onclick="hideDialog()" type="submit" class="btn btn-primary">Share</button>
<button onclick="activateShareButtons()" id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
There are two solutions to solve your problem :
Option 1 :
Since you have got the parameters(title, workbookId, id) , you can call server side function using AJAX to render the partial view , then replace the DIV contained in the partial view with the updated contents in the callback function of AJAX .
You can click here for code sample .
Option 2 :
Directly update related input/area using Jquery . For example , the input tag helper :
<input asp-for="<Expression Name>">
generates the id and name HTML attributes for the expression name specified in the asp-for attribute. So you can set the value using Jquery like :
$("#Title").val("Title")
Please click here for Tag Helpers in forms in ASP.NET Core
With Option 2 , you need to clear the Emails area firstly after user click the share button ; With Option 1 , you don't need to care that since the HTML will replace entirely .
I was trying to add additional custom field in the checkout screen and here is my code:
<div class="additional-checkout-fields" style="display:none">
<div class="fieldset fieldset--address-type" data-additional-fields>
<div class="field field--optional field--address-type">
<h2 class="additional-field-title">ADDRESS TYPE</h2>
<div class="field__input-wrapper">
<label>
<input data-backup="Residential" class="input-checkbox" aria-labelledby="error-for-address_type" type="checkbox" name="checkout[Residential]" id="checkout_attributes_Residential" value="Residential" />
<span>Residential</span>
</label>
<label>
<input data-backup="Commercial" class="input-checkbox" aria-labelledby="error-for-address_type" type="checkbox" name="checkout[Commercial]" id="checkout_attributes_Commercial" value="Commercial" />
<span>Commercial</span>
</label>
</div>
</div>
</div>
</div>
<script type="text/javascript">
if (window.jQuery) {
jquery = window.jQuery;
} else if (window.Checkout && window.Checkout.$) {
jquery = window.Checkout.$;
}
jquery(function() {
if (jquery('.section--shipping-address .section__content').length) {
var addType = jquery('.additional-checkout-fields').html();
jquery('.section--shipping-address .section__content').append(addType);
}
});
</script>
It returns the checkout page like this -
The problem is - once I click continue button and comes back to this page again, I don't see the checkbox checked. I feel the values are not being passed or may be something else.
What am I missing?
From the usecase, it looks like you want the user to select the Address Type either Residential or Commercial so a raido button group seems more suitable. I have edited the HTML to create the Radio Button instead of Checkbox. To maintain the state, I have used Session Storage. You may also replace Session Storage with Local Storage if you want to do so. For explanation check code comments.
<div class="additional-checkout-fields" style="display:none">
<div class="fieldset fieldset--address-type" data-additional-fields>
<div class="field field--optional field--address-type">
<h2 class="additional-field-title">ADDRESS TYPE</h2>
<div class="field__input-wrapper">
<label>
<input class="input-radio" aria-label="" type="radio" name="checkout[address_type]" id="checkout_attributes_Residential" value="residential" checked>
<span>Residential</span>
</label>
<label>
<input class="input-radio" aria-label="" type="radio"name="checkout[address_type]" id="checkout_attributes_Commercial" value="commercial">
<span>Commercial</span>
</label>
</div>
</div>
</div>
</div>
JavaScript part
<script type = "text/javascript" >
if (window.jQuery) {
jquery = window.jQuery;
} else if (window.Checkout && window.Checkout.$) {
jquery = window.Checkout.$;
}
jquery(function() {
if (jquery('.section--shipping-address .section__content').length) {
var addType = jquery('.additional-checkout-fields').html();
jquery('.section--shipping-address .section__content').append(addType);
// Get saved data from sessionStorage
let savedAddressType = sessionStorage.getItem('address_type');
// if some value exist in sessionStorage
if (savedAddressType !== null) {
jquery('input[name="checkout[address_type]"][value=' + savedAddressType + ']').prop("checked", true);
}
// Listen to change event on radio button
jquery('input[name="checkout[address_type]"]').change(function() {
if (this.value !== savedAddressType) {
savedAddressType = this.value;
sessionStorage.setItem('address_type', savedAddressType);
}
});
}
});
</script>
You are responsible for managing the state of your added elements. Shopify could care a less about stuff you add, so of course when you flip around between screens, it will be up to you to manage the contents. Use localStorage or a cookie. Works wonders. As a bonus exercise, ensure that your custom field values are assigned to the order when you finish a checkout. You might find all your hard work is for nothing as those value languish in la-la land unless you explicitly add them as order notes or attributes.
I need:
Acess /Client/Create
Add dynamically Partial Views (/Product/Card) and bind them to Client.Products
In each Partial View when i click in a button open a bootstrap modal windows where i can set Product's information
Close the modal and reflect changes of modal reflect in the Card's Product.
The problem is: how to change product informations in another view(other than Card) and reflect to the product of the card?
#using (Html.BeginCollectionItem("Products"))
{
#Html.HiddenFor(model => model.ClientID)
#Html.HiddenFor(model => model.ProductID)
<div class="card">
<img class="card-img-top" src="http://macbook.nl/wp-content/themes/macbook/images/png/iphone318x180.png" alt="Grupo Logo">
<div class="card-block">
<h4 class="card-title">#Model.Name</h4>
<p class="card-text">#Model.Desc</p>
<div class="btn-group">
<button type ="button" class="btn btn-primary open-modal" data-path="/Product/Edit/#Model.ProductID">Edit</button>
<button type="button" class="btn btn-primary open-modal" data-path="/Product/Features/#Model.ProductID">Features</button>
</div>
</div>
</div>
}
You can do this is another view (or by dynamically loading another view into a modal. The object has not been created yet, and since you using BeginCollectionItem() to generate new items, any other view you used would not be using the same Guid created by that helper so you would not be able to match up the collection items.
Instead, include the 'other' properties within the partial, but put them in a hidden element that gets displayed as a modal when you click the buttons.
The basic structure of the partial for adding/editing a Product would be
<div class="product">
#using (Html.BeginCollectionItem("Products"))
{
#Html.HiddenFor(m => m.ClientID)
#Html.HiddenFor(m => m.ProductID)
<div class="card">
....
<button type ="button" class="btn btn-primary edit">Edit</button>
</div>
// Modal for editing additional data
<div class="modal">
#Html.TxtBoxFor(m => m.SomeProperty)
#Html.TxtBoxFor(m => m.AnotherProperty)
....
</div>
}
</div>
And then handle the buttons .click() event (using delegation since the partials will be dynamically added to the view) to display the associated modal (assumes you have a <div id="products"> element that is the container for all Products)
$('#products').on('click', '.edit', function() {
var modal = $(this).closest('.product').find('.modal');
modal.show(); // display the modal
});
I have created a widget based template like,
<div class="content">
<div>
<!-- rest of content-->
</div>
<div class="col-md-6">
<div class="panel" data-dojo-attach-point="sysinfo">
<ul class="col-md-12 stats">
<li class="stat col-md-3 col-sm-3 col-xs-6">Host:</br> <span><b class="value">{{hname}}</b></span>
</li>
<li class="stat col-md-3 col-sm-3 col-xs-6"># CPU:</br> <span><b class="value">{{cpu}}</b></span>
</li>
</ul>
</div>
</div>
</div>
How do I update only content of sysinfo ?
Till now I was doing,
var widget = this;
widget.template = new dtl.Template(widget.templateString);
var template = widget.template;
template.update(node, stats); // but it update complete content as node == content. I just want to refresh sysinfo.
I also tried,
template.update(this.sysinfo, stats); // but it throws exceptions
Any ideas?
As far as I can see is that when you're using dojox/dtl/_Templated as suggested in the documentation, there is no update() function available.
If you really wish for certain things, you will have to manually define a template and render that one (and replace the attach point), for example:
var subtemplate = "<ul data-dojo-attach-point='sysinfo'>{% for item in list %}<li>{{item}}</li>{% endfor %}</ul>";
var template = "<div><h1>{{title}}</h1>" + subtemplate + "</div>";
var CustomList = declare("custom/List", [ _WidgetBase, _Templated, _DomTemplated ], {
templateString: template,
subTemplate: new dtl.Template(subtemplate),
title: "Fruits",
list: [ 'Apple', 'Banana', 'Lemon' ],
_setListAttr: function(list) {
this.list = list;
this.sysinfo = domConstruct.place(this.subTemplate.render(new dtl.Context(this)), this.sysinfo, "replace");
},
_getListAttr: function(list) {
return this.list;
}
});
Normally, if you would update the template when the list is set, you could use this.render() inside the _setListAttr() function to update the entire template.
However, as you can see in the _setListAttr() function I'm replacing an attach point by a newly rendered Django template.
This results in only that part of the template being updated, in stead of the entire template. So {{title}} would remain the original value, even when changed.
A full example can be found on JSFiddle: http://jsfiddle.net/pb3k3/