How to change css variable in shadow dom (web component) - web-component

I can't manage to change a css variable (--color) because the shadowRoot querySelector won't return :host or html
<style>
:host {display: block;
--color: ${this.color};
}
.colored {
color: var(--color);
}
</style>
This breaks (querySelector returns null)
this.shadowRoot.querySelector(':host').style.setProperty('--color', value);
This changes the color but is not want I want
this.shadowRoot.querySelector('.colored').style.setProperty('color', value);
And finally, this attempt does the trick, but it will only work as long as :host is the first rule and it is in the first stylesheet.
this.shadowRoot.styleSheets[0].rules[0].style.setProperty('--color', value);
<!DOCTYPE html>
<body>
<my-element></my-element>
<script>
class MyElement extends HTMLElement {
constructor() {
super();
this._color = "green";
this._shadowRoot = this.attachShadow({ mode: "open" });
this._shadowRoot.innerHTML = '';
this._shadowRoot.appendChild(this.template().content.cloneNode(true));
}
template() {
const template = document.createElement("template");
template.innerHTML = `
<style>
:host {display: block;
--color: ${this.color};
}
.colored {
color: var(--color);
}
</style>
<p>The color will change in 2 seconds: <span id="color" class="colored">${this.color}</span></p>
`;
return template;
}
get color() {
return this._color;
}
set color(value) {
this._color = value;
this.shadowRoot.getElementById('color').innerHTML = value;
// ▼ 👉🏻 Uncomment below. Cannot change --color variable; selector returns null
// this.shadowRoot.querySelector(':host').setProperty('--color', value);
// ▼ This works but is not what I want. I need to change the variable
//this.shadowRoot.querySelector('.colored').style.setProperty('color', value);
// ▼ This is subject to the firts stylesheet and :host being first rule
this.shadowRoot.styleSheets[0].rules[0].style.setProperty('--color', value);
}
}
customElements.define("my-element", MyElement);
setTimeout(() => {
document.querySelector('my-element').color = 'blue';
}, 2000);
</script>
</body>
</html>
Thanks!

:host refers to your <my-element>
So set properties on your my-element with: this.style.setProperty('--color', value);
<my-element></my-element>
<script>
customElements.define("my-element", class extends HTMLElement {
constructor() {
super()
.attachShadow({mode: "open"})
.innerHTML = `
<style>
:host {display: block;
--color: red;
}
.colored {
color: var(--color);
}
</style>
<p>The color will change in 2 seconds: <span id="color" class="colored">${this.color}</span></p>`;
this.color = "green";
}
get color() {
return this._color;
}
set color(value) {
this._color = value;
this.shadowRoot.getElementById('color').innerHTML = value;
this.style.setProperty('--color', value);
}
});
setTimeout(() => {
document.querySelector('my-element').color = 'blue';
}, 2000);
</script>

Related

toggle between two button style in react js

I want the button's style to change when I click on them.
I want the option 1 button to be selected and background color to change to by default. If I click on the option 2 button, I want the option 2 button to change and unchanged option 1.
I already used the method found in this post
However, the method doesn't seems to be working correctly since the color of my buttons is the default HTML button color.
My code:
import React, {Component} from 'react';
import './OptionButtons.css'
export class OptionButtons extends Component{
constructor() {
super();
this.state = {
selected: "btn1"
};
}
changeColor = (btn) => {
this.setState({ selected: btn });
};
addClass = (btn) => {
if (this.state.selected === btn) return "selected";
else return "notSelect";
};
render() {
return (
<div class = "option">
<h2> Options </h2>
<div class = "buttons">
<button id = "option1Btn" className = {this.addClass("btn1")} onClick = {this.changeColor.bind(this, "btn1")}> Option 1 </button>
<button className = {this.addClass("btn2")} onClick = {this.changeColor.bind(this, "btn2")}> Option 2 </button>
</div>
</div>
);
}
}
OptionButtons.css
.option {
box-sizing: border-box;
position: relative;
margin-top: 655px;
margin-left: auto;
margin-right: auto;
width: 80%;
max-width: 650px;
}
.option .buttons {
flex: 20%;
display: flex;
justify-content: center;
align-items: center;
}
.option .buttons button {
flex-direction: row;
border-radius: 5px;
padding: 0 1.3rem;
font-family: 'Nunito';
font-style: normal;
font-weight: 700;
font-size: 1.2rem;
line-height: 27px;
text-align: center;
width: 320px;
height: 40px;
left: 50px;
cursor: pointer;
}
#option1Btn{
margin-right: 10px;
}
.selected {
color: "#fff";
background-color: "#00867D";
border: 1px solid "#00867D";
}
.notSelected {
color: "#00867D";
background-color: "#F2F2F2";
border: 1px solid "#F2F2F2";
}
Result of my code
I am not sure. did you say that when you click button 2, Its color will be changed and button 1 will be unchanged(active always)? or button 1 will be inactive?
https://codesandbox.io/s/priceless-tamas-yjr1lw?file=/src/Some.js
check it, do you want like this?
You can easily change colors on button clicks in react:
const [backgroundColor, setBackgroundColor] = useState("white");
const [buttonColors, setButtonColors] = useState({
color1: "red",
color2: "green"
});
const button1Clicked = () => {
setBackgroundColor("gray");
setButtonColors({
...buttonColors,
color1: "green"
});
};
const button2Clicked = () => {
setBackgroundColor("green");
setButtonColors({
...buttonColors,
color2: "red"
});
};
return (
<div
className="App"
style={{
backgroundColor
}}
>
<button
style={{ backgroundColor: buttonColors.color1 }}
onClick={button1Clicked}
>
Button 1
</button>
<button
style={{ backgroundColor: buttonColors.color2 }}
onClick={button2Clicked}
>
Button 2
</button>
</div>
);
You can find a working example here.
There are a couple things that are not ok with your react code and CSS.
Number one you have elements with the property class instead of className.
Second, the color codes you are trying to attribute are strings ("#000"), so your browser doesn't know what to do with them. They should either be clear hexadecimals or using the rgb function.
I'll leave a Codesandbox using your component/CSS so you can take a look.
I think what you are doing is just wrong and very confusing as there is a very elegant way to do it.
import React, {
Component
} from 'react';
import './OptionButtons.css'
export class OptionButtons extends Component {
constructor() {
super();
this.state = {
selected: "btn1" //make this state a boolean so that it will turn true when you press button 1 and false when you press button 2 or vice versa
};
}
changeColor = (btn) => {
this.setState({
selected: btn
});
};
//I have read it many times but I still don't understand what this function does in the code. In my opinion, it has no value to the action you are trying to achieve.
addClass = (btn) => {
if (this.state.selected === btn) return "selected";
else return "notSelect";
};
render() {
return ( <
div class = "option" >
<
h2 > Options < /h2> <
div class = "buttons" >
//it its better and easy to use a turnery operator rather than a function
<
button id = "option1Btn"
className = {
this.addClass("btn1")
}
onClick = {
this.changeColor.bind(this, "btn1")
} > Option 1 < /button> <
button className = {
this.addClass("btn2")
}
onClick = {
this.changeColor.bind(this, "btn2")
} > Option 2 < /button> <
/div> <
/div>
);
}
Instead, you can do like this
import React, {
Component
} from 'react';
import './OptionButtons.css'
export class OptionButtons extends Component {
constructor() {
super();
this.state = {
selected: null //null so that when you click, you can assign a boolean value
};
}
changeColorBtn1 = () => {
this.setState({
selected: true
})
}
changeColorBtn2 = () => {
this.setState({
selected: false
})
}
render() {
return ( <
div class = "option" >
<
h2 > Options < /h2> <
div class = "buttons" >
//I'm using turnery expression which means if selected: true then classname will be btn1
<
button id = "option1Btn"
className = {
this.state.selected && 'btn1'
}
onClick = {
this.changeColorBtn2.bind(this, "btn1")
} > Option 1 < /button>
//same as I did in the previous button but it's vice-versa
<
button className = {!this.state.selected && 'btn2'
}
onClick = {
this.changeColorBtn2.bind(this)
} > Option 2 < /button> <
/div> <
/div>
);
}
I hope it answers your question

Is it possible to use styles from an Angular service in scss?

I have a change-color.service.ts that has the following:
public defaultStyles = {
firstDesignBackgroundColor: '#a31329',
firstDesignFontColor: '#ffffff',
secondDesignBackgroundColor: '#d1cfcfff',
secondDesignFontColor: '#000000'
};
now I will like to add to my style.scss for the statement
:host ::ng-deep th span#optimize-checkbox-header .mat-checkbox label.mat-checkbox-layout .mat-checkbox-inner-container.mat-checkbox-inner-container-no-side-margin .mat-checkbox-frame {
border: 2px solid #fff !important;
}
replace the #fff with firstDesignFontColor from the change-service. Do you know how I can create this dependency? Is this possible at all?
There is actually a way to realize it with css variables that I will post here as a second answer.
You can change css variables from JavaScript code, so if you use variables for your class like this simplified example:
:root {
--bg-color: red;
}
.test {
background-color: var(--bg-color);
}
then you can change this from your ChangeColorService
interface Colors {
background: string;
}
#Injectable()
export class ChangeColorService {
colors$ = new BehaviorSubject<Colors>({ background: 'red' });
constructor(#Inject(DOCUMENT) private document: Document) { }
change(colors: Colors) {
const root = this.document.documentElement;
root.style.setProperty('--bg-color', colors.background);
this.colors$.next(colors);
}
}
Full example:
https://stackblitz.com/edit/angular-change-css-variable?file=src/app/app.component.ts
No that's not possible. What is possible, is to use style bindings.
class YourComponent {
styles: any;
constructor(private color: ChangeColor) {}
ngOnInit() { this.styles = this.color.defaultStyles; }
}
<div [style.background-color]="styles.firstDesignBackgroundColor"></div>

How can I use data in css with Vue?

I want to design a button component with Vue.
When I get main props, how can I get in css --mainColor variable without setting inline-style? thanks.
<script>
import Vue from "vue";
export default Vue.extend({
name: "Button",
props: {
text: {
type: String,
default: ""
},
main: {
type: String,
default: "#10b981"
}
}
});
</script>
<style lang="scss" scoped>
.btn {
--mainColor: #10b981;
display: inline-block;
border: 1px solid var(--mainColor);
color: var(--mainColor);
&:hover {
background-color: var(--mainColor);
color: #fff;
}
}
</style>
I doubt that's possible. But you can run conditional logic in sass, like their official example:
$light-background: #f2ece4;
$light-text: #036;
$dark-background: #6b717f;
$dark-text: #d2e1dd;
#mixin theme-colors($light-theme: true) {
#if $light-theme {
background-color: $light-background;
color: $light-text;
} #else {
background-color: $dark-background;
color: $dark-text;
}
}
.banner {
#include theme-colors($light-theme: true);
body.dark & {
#include theme-colors($light-theme: false);
}
}
https://sass-lang.com/documentation/at-rules/control/if
This is super easy if using Vue3:
<template>
<h1>HELLO</h1>
</template>
<script>
export default {
data() {
return {
h1Color: "red",
};
},
};
</script>
<style>
h1 {
color: v-bind(h1Color);
}
</style>
For a more extensive guide - here
If using Vue2 I would suggest using the same approach as in Steven B's answer, here the ref

Animate a quizz app with AngularJS

I had done one quiz application, But i want to add some animations
like fadein/fade-out, when click the prev/next button. Can any one
help me do the same. something need to change the css something need to change the CSS something need to change the css something need to change the css?
* {}
body {}
.question {
width: 70%;
margin: 0 auto;
height: auto;
display: block;
background: #eeeeee;
}
.question h1 {
text-align: center;
padding-top: 30px;
color: #666666;
}
.question h2 {
width: 100%;
font-size: 22px;
color: #0c1e5c;
padding: 1% 3% 0% 3%;
}
.question ul:nth-child(odd) {
background: #d0dff6;
width: 30%;
padding: 8px;
margin: 1% 9%;
display: inline-block;
color: #0c1e5c;
}
.question ul:nth-child(even) {
background: #d0dff6;
width: 30%;
padding: 8px;
margin: 1% 9%;
display: inline-block;
color: #0c1e5c;
}
.button {
text-align: center;
margin: 1% 0;
}
.btn {
background: #8bf8a7;
padding: 5px;
}
<html ng-app="quiz">
<head>
<meta charset="utf-8">
<title>Basic Quiz</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body ng-controller="quizCtrl">
<div class="question">
<h1>QUIZ APPLICATION</h1>
<h2>{{questions.question}}</h2>
<ul ng-repeat="option in questions.options">
<li style="list-style:none">
<input type="{{buttonType}}">{{option.text}}</li>
</ul>
</div>
<div class="button">
<input type="button" value="previous" class="btn" ng-show="isPrevious" ng-click="previousQuestion()">
<input type="button" value="next" class="btn" ng-show="isNext" ng-click="nextQuestion()">
</div>
</body>
<script>
var app = angular.module("quiz", [])
app.controller("quizCtrl", function($scope) {
$scope.data = [{
question: "1)Which of the following selector matches a element based on its id?",
type: "single",
options: [{
text: "The Id Selector"
},
{
text: "The Universal Selector"
},
{
text: "The Descendant Selector"
},
{
text: "The Class Selector"
}
]
},
{
question: "2)Which of the following defines a measurement as a percentage relative to another value, typically an enclosing element?",
type: "multiple",
options: [{
text: "%"
},
{
text: "cm"
},
{
text: "percentage"
},
{
text: "ex"
}
]
},
{
question: "3)Which of the following property is used to set the background color of an element?",
type: "single",
options: [{
text: "background-color"
},
{
text: "background-image"
},
{
text: "background-repeat"
},
{
text: "background-position"
}
]
},
{
question: "4)Which of the following is a true about CSS style overriding?",
type: "multiple",
options: [{
text: "Any inline style sheet takes highest priority. So, it will override any rule defined in tags or rules defined in any external style sheet file."
},
{
text: "Any rule defined in tags will override rules defined in any external style sheet file."
},
{
text: "Any rule defined in external style sheet file takes lowest priority, and rules defined in this file will be applied only when above two rules are not applicable."
}
]
}
];
$scope.index = 0;
$scope.questions = $scope.data[$scope.index];
$scope.buttonType = $scope.questions.type == 'single' ? 'radio' : 'checkbox';
$scope.isPrevious = false;
$scope.isNext = true;
$scope.nextQuestion = function() {
if ($scope.index < 3) {
$scope.index = $scope.index + 1;
$scope.questions = $scope.data[$scope.index];
$scope.buttonType = $scope.questions.type == 'single' ? 'radio' : 'checkbox';
$scope.isPrevious = true;
if ($scope.index == 3) {
$scope.isNext = false;
}
} else {
// disble next botton logic
$scope.isNext = false;
}
}
$scope.previousQuestion = function() {
if ($scope.index > 0) {
$scope.index = $scope.index - 1;
$scope.questions = $scope.data[$scope.index];
$scope.buttonType = $scope.questions.type == 'single' ? 'radio' : 'checkbox';
$scope.isNext = true;
if ($scope.index == 0) {
$scope.isPrevious = false;
}
} else {
// disble next botton logic
$scope.isPrevious = false;
}
}
});
</script>
</html>
Check out ng-animate, basically what it does is it adds classes that you can style accordingly on showing dom and on hiding dom, like this:
/* The starting CSS styles for the enter animation */
.fade.ng-enter {
transition:0.5s linear all;
opacity:0;
}
/* The finishing CSS styles for the enter animation */
.fade.ng-enter.ng-enter-active {
opacity:1;
}
And to use that functionality you would have to use ng-repeat in your html, something like this:
<div ng-repeat="item in data" ng-if="index === $index">
//Your question html here
</div>
Where data and index are $scope.data and $scope.index.
That would be the angular way of doing things.
However I see that you are using the same div, only changing scope data, that would require you to set
transition: 1s all ease;
On the question class, and then to do something like this in javascript:
angular.element('.question').css('opacity', 0);
$timeout(function() {
// change question..
angular.element('.question').css('opacity', 1);
}, 1000);

Apply Twitter Bootstrap Validation Style and Message to ASP.NET MVC validation

How can I integrate ASP.NET MVC unobtrusive validation and Twitter Bootstrap? I want to have all those validation messages and styles appropriately.
A nice way of handling this if you're using Bootstrap 2 is...
Add this to your _Layout.cshtml:
<script type="text/javascript">
jQuery.validator.setDefaults({
highlight: function (element, errorClass, validClass) {
if (element.type === 'radio') {
this.findByName(element.name).addClass(errorClass).removeClass(validClass);
} else {
$(element).addClass(errorClass).removeClass(validClass);
$(element).closest('.control-group').removeClass('success').addClass('error');
}
},
unhighlight: function (element, errorClass, validClass) {
if (element.type === 'radio') {
this.findByName(element.name).removeClass(errorClass).addClass(validClass);
} else {
$(element).removeClass(errorClass).addClass(validClass);
$(element).closest('.control-group').removeClass('error').addClass('success');
}
}
});
$(function () {
$("span.field-validation-valid, span.field-validation-error").addClass('help-inline');
$("div.control-group").has("span.field-validation-error").addClass('error');
$("div.validation-summary-errors").has("li:visible").addClass("alert alert-block alert-error");
});
</script>
These are the posts where I found the code pieces above:
Integrating Bootstrap Error styling with MVC’s Unobtrusive Error Validation
Twitter Bootstrap validation styles with ASP.NET MVC
MVC Twitter Bootstrap unobtrusive error handling
UPDATE
Right now I needed to do the same when using Bootstrap 3. Here's the modifications necessary since the class names changed:
<script type="text/javascript">
jQuery.validator.setDefaults({
highlight: function (element, errorClass, validClass)
{
if (element.type === 'radio')
{
this.findByName(element.name).addClass(errorClass).removeClass(validClass);
} else
{
$(element).addClass(errorClass).removeClass(validClass);
$(element).closest('.form-group').removeClass('has-success').addClass('has-error');
}
},
unhighlight: function (element, errorClass, validClass)
{
if (element.type === 'radio')
{
this.findByName(element.name).removeClass(errorClass).addClass(validClass);
} else
{
$(element).removeClass(errorClass).addClass(validClass);
$(element).closest('.form-group').removeClass('has-error').addClass('has-success');
}
}
});
$(function () {
$("span.field-validation-valid, span.field-validation-error").addClass('help-block');
$("div.form-group").has("span.field-validation-error").addClass('has-error');
$("div.validation-summary-errors").has("li:visible").addClass("alert alert-block alert-danger");
});
</script>
Copy the css of the validators in your css file and change the color accordinlgly.
Something like this should do
.field-validation-error {
color: #b94a48;
display: inline-block;
*display: inline;
padding-left: 5px;
vertical-align: middle;
*zoom: 1;
}
.field-validation-valid {
display: none;
}
.input-validation-error {
/*
border: 1px solid #ff0000;
background-color: #ffeeee;
*/
color: #b94a48;
border-color: #b94a48;
}
.input-validation-error:focus {
border-color: #953b39;
-webkit-box-shadow: 0 0 6px #d59392;
-moz-box-shadow: 0 0 6px #d59392;
box-shadow: 0 0 6px #d59392;
}
.validation-summary-errors {
/*font-weight: bold;*/
color: #b94a48;
}
.validation-summary-valid {
display: none;
}
I suggest to include Bootstrapper in less format and do the same thing as Iridio suggested but in .less.
That way you could have something like:
.validation-summary-errors
{
.alert();
.alert-error();
}
.field-validation-error
{
.label();
.label-important();
}
so when bootstrapper will change you'll pick up the changes automatically.
Regular styles that handle visibility from MVC default Site.css will stay in place and handle visibility.
Why not just use css !important and call it a day:
/* Styles for validation helpers
-----------------------------------------------------------*/
.field-validation-error {
color: #f00 !important;
}
.field-validation-valid {
display: none;
}
.input-validation-error {
border: 1px solid #f00 !important;
background-color: #fee !important;
}
.validation-summary-errors {
font-weight: bold;
color: #f00;
}
.validation-summary-valid {
display: none;
}
On Bootstrap 3 you have to add:
.validation-summary-errors
{
.alert();
.alert-danger();
}
.field-validation-error
{
.label();
.label-danger();
}
you'll see something like that:
For the ValidationSummary, you can use the overload that allows you to specify htmlAttributes. This allows you to set it to use the Twitter Bootstrap alert css styles.
#Html.ValidationSummary(string.Empty, new { #class = "alert alert-danger" })
A similar overload exists for the ValidationMessage and ValidationMessageFor helper methods.
You can integrate MVC3 validation with Bootstrap framework by adding the following javascript to your page (View)
<script>
$(document).ready(function () {
/* Bootstrap Fix */
$.validator.setDefaults({
highlight: function (element) {
$(element).closest("div.control-group").addClass("error");
},
unhighlight: function (element) {
$(element).closest("div.control-group").removeClass("error");
}
});
var current_div;
$(".editor-label, .editor-field").each(function () {
var $this = $(this);
if ($this.hasClass("editor-label")) {
current_div = $('<div class="control-group"></div>').insertBefore(this);
}
current_div.append(this);
});
$(".editor-label").each(function () {
$(this).contents().unwrap();
});
$(".editor-field").each(function () {
$(this).addClass("controls");
$(this).removeClass("editor-field");
});
$("label").each(function () {
$(this).addClass("control-label");
});
$("span.field-validation-valid, span.field-validation-error").each(function () {
$(this).addClass("help-inline");
});
$("form").each(function () {
$(this).addClass("form-horizontal");
$(this).find("div.control-group").each(function () {
if ($(this).find("span.field-validation-error").length > 0) {
$(this).addClass("error");
}
});
});
});
</script>
Besides, on the Views (for example "Create.cshtml") make sure that the fields in the form are formatted as the following...
<div class="editor-label">
#Html.LabelFor(Function(model) model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(Function(model) model.Name)
#Html.ValidationMessageFor(Function(model) model.Name)
</div>
For those using Bootstrap 3, the css classes have changed and the solutions above need modifications to work with Bootstrap 3. I have used the following with success with MVC 4 and Bootstrap 3. See this SO thread for more:
$(function () {
// any validation summary items should be encapsulated by a class alert and alert-danger
$('.validation-summary-errors').each(function () {
$(this).addClass('alert');
$(this).addClass('alert-danger');
});
// update validation fields on submission of form
$('form').submit(function () {
if ($(this).valid()) {
$(this).find('div.control-group').each(function () {
if ($(this).find('span.field-validation-error').length == 0) {
$(this).removeClass('has-error');
$(this).addClass('has-success');
}
});
}
else {
$(this).find('div.control-group').each(function () {
if ($(this).find('span.field-validation-error').length > 0) {
$(this).removeClass('has-success');
$(this).addClass('has-error');
}
});
$('.validation-summary-errors').each(function () {
if ($(this).hasClass('alert-danger') == false) {
$(this).addClass('alert');
$(this).addClass('alert-danger');
}
});
}
});
// check each form-group for errors on ready
$('form').each(function () {
$(this).find('div.form-group').each(function () {
if ($(this).find('span.field-validation-error').length > 0) {
$(this).addClass('has-error');
}
});
});
});
var page = function () {
//Update the validator
$.validator.setDefaults({
highlight: function (element) {
$(element).closest(".form-group").addClass("has-error");
$(element).closest(".form-group").removeClass("has-success");
},
unhighlight: function (element) {
$(element).closest(".form-group").removeClass("has-error");
$(element).closest(".form-group").addClass("has-success");
}
});
}();
You can add a few classes to your Site.css file:
/* styles for validation helpers */
.field-validation-error {
color: #b94a48;
}
.field-validation-valid {
display: none;
}
input.input-validation-error {
border: 1px solid #b94a48;
}
select.input-validation-error {
border: 1px solid #b94a48;
}
input[type="checkbox"].input-validation-error {
border: 0 none;
}
.validation-summary-errors {
color: #b94a48;
}
.validation-summary-valid {
display: none;
}
FYI: http://weblogs.asp.net/jdanforth/form-validation-formatting-in-asp-net-mvc-5-and-bootstrap-3
This will convert ValidationSummary() to a boostrap alert. You can include a little script to remove unnecessary classes and give a highlight to fields with problems.
#if (ViewData.ModelState.Any(x => x.Value.Errors.Any())) {
<div class="alert alert-danger">
×
<h4>Validation Errors</h4>
#Html.ValidationSummary()
</div>
}
<script>
$(".validation-summary-errors").removeClass("validation-summary-errors");
$(".input-validation-error").removeClass("input-validation-error").parent().addClass("has-error");
</script>
See more information at http://chadkuehn.com/convert-razor-validation-summary-into-bootstrap-alert/
This is a neat solution that gives you more control over how the ValidationSummary renders errors to the view. The Unordered List it produced did not look right inside the alert. Therefore, I simply looped through the errors and rendered them how I wanted - using paragraphs in this case. For example:
#if (ViewData.ModelState.Any(x => x.Value.Errors.Any()))
{
<div class="alert alert-danger" role="alert">
<a class="close" data-dismiss="alert">×</a>
#foreach (var modelError in Html.ViewData.ModelState.SelectMany(keyValuePair => keyValuePair.Value.Errors))
{
<p>#modelError.ErrorMessage</p>
}
</div>
}
Which results in a neat Validation Summary Alert:
The following worked for me:
$(function () {
// any validation summary items should be encapsulated by a class alert and alert-danger
$('.validation-summary-errors').each(function () {
$(this).addClass('alert');
$(this).addClass('alert-danger');
});
// update validation fields on submission of form
$('form').submit(function () {
if ($(this).valid()) {
$(this).find('div.control-group').each(function () {
if ($(this).find('span.field-validation-error').length == 0) {
$(this).removeClass('has-error');
$(this).addClass('has-success');
}
});
}
else {
$(this).find('div.control-group').each(function () {
if ($(this).find('span.field-validation-error').length > 0) {
$(this).removeClass('has-success');
$(this).addClass('has-error');
}
});
$('.validation-summary-errors').each(function () {
if ($(this).hasClass('alert-danger') == false) {
$(this).addClass('alert');
$(this).addClass('alert-danger');
}
});
}
});
// check each form-group for errors on ready
$('form').each(function () {
$(this).find('div.form-group').each(function () {
if ($(this).find('span.field-validation-error').length > 0) {
$(this).addClass('has-error');
}
});
});
});
var page = function () {
//Update the validator
$.validator.setDefaults({
highlight: function (element) {
$(element).closest(".form-group").addClass("has-error");
$(element).closest(".form-group").removeClass("has-success");
},
unhighlight: function (element) {
$(element).closest(".form-group").removeClass("has-error");
$(element).closest(".form-group").addClass("has-success");
}
});
}();
Taken from http://www.benripley.com/development/javascript/asp-mvc-4-validation-with-bootstrap-3.

Resources